11package com.tans.tfiletransporter.ui.activity.folderselect
22
3+ import android.app.Activity
4+ import android.content.Intent
5+ import android.view.View
6+ import androidx.activity.OnBackPressedCallback
7+ import androidx.activity.addCallback
8+ import androidx.recyclerview.widget.LinearLayoutManager
9+ import com.jakewharton.rxbinding4.swiperefreshlayout.refreshes
310import com.jakewharton.rxbinding4.view.clicks
11+ import com.tans.tadapter.adapter.DifferHandler
12+ import com.tans.tadapter.recyclerviewutils.MarginDividerItemDecoration
13+ import com.tans.tadapter.spec.SimpleAdapterSpec
14+ import com.tans.tadapter.spec.toAdapter
415import com.tans.tfiletransporter.R
16+ import com.tans.tfiletransporter.Settings
17+ import com.tans.tfiletransporter.databinding.FolderItemLayoutBinding
518import com.tans.tfiletransporter.databinding.FolderSelectActivityBinding
19+ import com.tans.tfiletransporter.file.FileLeaf
20+ import com.tans.tfiletransporter.file.FileTree
21+ import com.tans.tfiletransporter.file.createLocalRootTree
22+ import com.tans.tfiletransporter.file.isRootFileTree
23+ import com.tans.tfiletransporter.file.newLocalSubTree
24+ import com.tans.tfiletransporter.ui.DataBindingAdapter
625import com.tans.tfiletransporter.ui.activity.BaseActivity
26+ import com.tans.tfiletransporter.ui.activity.commomdialog.loadingDialog
27+ import com.tans.tfiletransporter.ui.activity.commomdialog.showNoOptionalDialog
28+ import com.tans.tfiletransporter.utils.dp2px
29+ import com.tans.tfiletransporter.utils.firstVisibleItemPosition
30+ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
731import io.reactivex.rxjava3.core.Single
32+ import io.reactivex.rxjava3.kotlin.withLatestFrom
33+ import io.reactivex.rxjava3.schedulers.Schedulers
34+ import kotlinx.coroutines.Dispatchers
35+ import kotlinx.coroutines.channels.Channel
36+ import kotlinx.coroutines.launch
37+ import kotlinx.coroutines.rx3.await
38+ import kotlinx.coroutines.rx3.rxSingle
39+ import kotlinx.coroutines.withContext
40+ import java.io.File
41+ import java.util.ArrayDeque
42+ import java.util.Deque
843
944class FolderSelectActivity : BaseActivity <FolderSelectActivityBinding , FolderSelectActivity .Companion .FolderSelectState >(
1045 layoutId = R .layout.folder_select_activity,
1146 defaultState = FolderSelectState ()
1247) {
1348
49+ private val recyclerViewScrollChannel = Channel <Int >(1 )
50+
51+ private val folderPositionDeque: Deque <Int > = ArrayDeque ()
52+
53+ private val onBackPressedCallback: OnBackPressedCallback by lazy {
54+ onBackPressedDispatcher.addCallback {
55+ launch {
56+ updateState { state ->
57+ val i = folderPositionDeque.poll()
58+ if (i != null ) {
59+ recyclerViewScrollChannel.trySend(i).isSuccess
60+ }
61+ if (state.fileTree.parentTree == null ) state else FolderSelectState (
62+ state.fileTree.parentTree
63+ )
64+ }.await()
65+ }
66+ }
67+ }
68+
1469 override fun firstLaunchInitData () {
1570
71+ launch(Dispatchers .IO ) {
72+ updateState {
73+ it.copy(fileTree = createLocalRootTree(this @FolderSelectActivity))
74+ }.await()
75+ }
1676 }
1777
1878 override fun initViews (binding : FolderSelectActivityBinding ) {
1979
80+ render({ it.fileTree.path }) {
81+ binding.pathTv.text = it
82+ }.bindLife()
83+
84+ bindState()
85+ .distinctUntilChanged()
86+ .doOnNext {
87+ onBackPressedCallback.isEnabled = ! it.fileTree.isRootFileTree()
88+ }
89+ .bindLife()
90+
2091 binding.toolBar.menu.findItem(R .id.create_new_folder).clicks()
2192 .flatMapSingle {
2293 // TODO: create new folder.
2394 Single .just(Unit )
2495 }
2596 .bindLife()
97+
98+ binding.folderRv.adapter = SimpleAdapterSpec <FileLeaf .DirectoryFileLeaf , FolderItemLayoutBinding >(
99+ layoutId = R .layout.folder_item_layout,
100+ bindData = { _, data, lBinding ->
101+ lBinding.titleTv.text = data.name
102+ DataBindingAdapter .dateText(lBinding.modifiedDateTv, data.lastModified)
103+ lBinding.filesCountTv.visibility = View .INVISIBLE
104+ },
105+ dataUpdater = bindState().map { it.fileTree.dirLeafs },
106+ differHandler = DifferHandler (
107+ itemsTheSame = { a, b -> a.path == b.path },
108+ contentTheSame = { a, b -> a == b }
109+ ),
110+ itemClicks = listOf { lBinding, _ ->
111+ lBinding.root to { _, data ->
112+ rxSingle(Dispatchers .IO ) {
113+ val i = withContext(Dispatchers .Main ) {
114+ binding.folderRv.firstVisibleItemPosition()
115+ }
116+ folderPositionDeque.push(i)
117+ updateState { oldState ->
118+ oldState.copy(fileTree = oldState.fileTree.newLocalSubTree(data))
119+ }.await()
120+ Unit
121+ }
122+ .observeOn(AndroidSchedulers .mainThread())
123+ .loadingDialog(this )
124+ }
125+ }).toAdapter { list ->
126+ val position = recyclerViewScrollChannel.tryReceive().getOrNull()
127+ if (position != null && position < list.size) {
128+ (binding.folderRv.layoutManager as ? LinearLayoutManager )?.scrollToPositionWithOffset(
129+ position,
130+ 0
131+ )
132+ }
133+ }
134+
135+ binding.folderRv.addItemDecoration(
136+ MarginDividerItemDecoration .Companion .Builder ()
137+ .divider(MarginDividerItemDecoration .Companion .ColorDivider (getColor(R .color.line_color), dp2px(1 )))
138+ .marginStart(dp2px(65 ))
139+ .build()
140+ )
141+
142+ binding.refreshLayout.setColorSchemeResources(R .color.teal_200)
143+ binding.refreshLayout.refreshes()
144+ .observeOn(Schedulers .io())
145+ .flatMapSingle {
146+ updateState { oldState ->
147+ val oldTree = oldState.fileTree
148+ if (oldTree.isRootFileTree()) {
149+ oldState.copy(fileTree = createLocalRootTree(this ))
150+ } else {
151+ val parentTree = oldTree.parentTree
152+ val dirLeaf = parentTree?.dirLeafs?.find { it.path == oldTree.path }
153+ if (parentTree != null && dirLeaf != null ) {
154+ oldState.copy(
155+ fileTree = parentTree.newLocalSubTree(dirLeaf)
156+ )
157+ } else {
158+ oldState
159+ }
160+ }
161+ }.observeOn(AndroidSchedulers .mainThread())
162+ .doFinally {
163+ binding.refreshLayout.isRefreshing = false
164+ }
165+ }
166+ .bindLife()
167+
168+ binding.doneActionBt.clicks()
169+ .withLatestFrom(bindState().map { it.fileTree })
170+ .flatMapSingle { (_, tree) ->
171+ rxSingle(Dispatchers .Main ) {
172+ if (tree.isRootFileTree()) {
173+ this @FolderSelectActivity.showNoOptionalDialog(
174+ title = getString(R .string.folder_select_error_title),
175+ message = getString(R .string.folder_select_error_body, " Root fold can't be selected." )
176+ ).await()
177+ } else {
178+ if (withContext(Dispatchers .IO ) { Settings .isDirWriteable(tree.path) }) {
179+ val i = Intent ()
180+ i.putExtra(FOLDER_SELECT_KEY , tree.path)
181+ this @FolderSelectActivity.setResult(Activity .RESULT_OK , i)
182+ finish()
183+ } else {
184+ this @FolderSelectActivity.showNoOptionalDialog(
185+ title = getString(R .string.folder_select_error_title),
186+ message = getString(R .string.folder_select_error_body, " Can't write in ${tree.path} " )
187+ ).await()
188+ }
189+ }
190+ }
191+ }
192+ .bindLife()
26193 }
27194
28195 companion object {
196+
197+ private const val FOLDER_SELECT_KEY = " folder_select_key"
198+
29199 data class FolderSelectState (
30- val u : Unit = Unit
200+ val fileTree : FileTree = FileTree (
201+ dirLeafs = emptyList(),
202+ fileLeafs = emptyList(),
203+ path = File .separator,
204+ parentTree = null
205+ )
31206 )
207+
208+ fun getResult (i : Intent ): String? {
209+ return i.getStringExtra(FOLDER_SELECT_KEY )
210+ }
32211 }
33212}
0 commit comments