diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt index 547ede4f0..d55dee823 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt @@ -40,15 +40,11 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.ActivityRemoteFileBrowserBinding import com.nextcloud.talk.interfaces.SelectionInterface import com.nextcloud.talk.remotefilebrowser.adapters.RemoteFileBrowserItemsAdapter -import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel import com.nextcloud.talk.ui.dialog.SortingOrderDialogFragment import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.FileSortOrder import com.nextcloud.talk.utils.database.user.UserUtils -import java.io.File -import java.util.Collections -import java.util.TreeSet import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -66,8 +62,6 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe private var filesSelectionDoneMenuItem: MenuItem? = null - private val selectedPaths: MutableSet = Collections.synchronizedSet(TreeSet()) - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) @@ -132,13 +126,10 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe showGrid = showGrid, mimeTypeSelectionFilter = mimeTypeSelectionFilter, userEntity = userUtils.currentUser!!, - selectionInterface = this - ) { remoteFileBrowserItem -> - onItemClicked(remoteFileBrowserItem) - } - .apply { - items = remoteFileBrowserItems - } + selectionInterface = this, + onItemClicked = viewModel::onItemClicked + ) + adapter.items = remoteFileBrowserItems binding.recyclerView.adapter = adapter binding.recyclerView.layoutManager = layoutManager @@ -146,6 +137,9 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe showList() } + is RemoteFileBrowserItemsViewModel.FinishState -> { + finishWithResult(state.selectedPaths) + } } } @@ -160,24 +154,20 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe supportActionBar?.title = path } } + + viewModel.selectedPaths.observe(this) { selectedPaths -> + filesSelectionDoneMenuItem?.isVisible = !selectedPaths.isNullOrEmpty() + binding.recyclerView.adapter?.notifyDataSetChanged() + } } override fun onCreateOptionsMenu(menu: Menu?): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.menu_share_files, menu) filesSelectionDoneMenuItem = menu?.findItem(R.id.files_selection_done) - filesSelectionDoneMenuItem?.isVisible = selectedPaths.size > 0 return true } - private fun onItemClicked(remoteFileBrowserItem: RemoteFileBrowserItem) { - if ("inode/directory" == remoteFileBrowserItem.mimeType) { - viewModel.changePath(remoteFileBrowserItem.path!!) - } else { - toggleBrowserItemSelection(remoteFileBrowserItem.path!!) - } - } - override fun onResume() { super.onResume() refreshCurrentPath() @@ -199,7 +189,7 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe true } R.id.files_selection_done -> { - onFileSelectionDone() + viewModel.onSelectionDone() true } else -> { @@ -208,7 +198,7 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe } } - private fun onFileSelectionDone() { + private fun finishWithResult(selectedPaths: Set) { val data = Intent() data.putStringArrayListExtra(EXTRA_SELECTED_PATHS, ArrayList(selectedPaths)) setResult(Activity.RESULT_OK, data) @@ -243,35 +233,6 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe viewModel.loadItems() } - private fun shouldPathBeSelectedDueToParent(currentPath: String): Boolean { - var file = File(currentPath) - if (selectedPaths.size > 0 && file.parent != "/") { - while (file.parent != null) { - var parent = file.parent!! - if (File(file.parent!!).parent != null) { - parent += "/" - } - if (selectedPaths.contains(parent)) { - return true - } - file = File(file.parent!!) - } - } - return false - } - - private fun checkAndRemoveAnySelectedParents(currentPath: String) { - var file = File(currentPath) - selectedPaths.remove(currentPath) - while (file.parent != null) { - selectedPaths.remove(file.parent!! + "/") - file = File(file.parent!!) - } - runOnUiThread { - binding.recyclerView.adapter!!.notifyDataSetChanged() - } - } - companion object { private val TAG = RemoteFileBrowserActivity::class.simpleName const val SPAN_COUNT: Int = 4 @@ -280,20 +241,15 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe } override fun toggleBrowserItemSelection(path: String) { - if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) { - checkAndRemoveAnySelectedParents(path) - } else { - // TODO if it's a folder, remove all the children we added manually - selectedPaths.add(path) - } - filesSelectionDoneMenuItem?.isVisible = selectedPaths.size > 0 + // unused, viewmodel gets called directly } override fun isPathSelected(path: String): Boolean { - return selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path) + // TODO figure out a better way to do this. Narrower interface? + return viewModel.isPathSelected(path) } override fun shouldOnlySelectOneImageFile(): Boolean { - return true + return true // unused } } diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt index d98cdefae..8efa8770b 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt @@ -36,6 +36,19 @@ import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener import java.io.File import javax.inject.Inject +/** + * @startuml + * hide empty description + * [*] --> InitialState + * InitialState --> LoadingItemsState + * LoadingItemsState --> NoRemoteFileItemsState + * NoRemoteFileItemsState --> LoadingItemsState + * LoadingItemsState --> LoadedState + * LoadedState --> LoadingItemsState + * LoadedState --> FinishState + * FinishState --> [*] + * @enduml + */ class RemoteFileBrowserItemsViewModel @Inject constructor( private val repository: RemoteFileBrowserItemsRepository, private val appPreferences: AppPreferences @@ -47,6 +60,7 @@ class RemoteFileBrowserItemsViewModel @Inject constructor( object NoRemoteFileItemsState : ViewState object LoadingItemsState : ViewState class LoadedState(val items: List) : ViewState + class FinishState(val selectedPaths: Set) : ViewState private val initialSortOrder = FileSortOrderNew.getFileSortOrder(appPreferences.sorting) private val sortingPrefListener: SortChangeListener = SortChangeListener() @@ -64,6 +78,10 @@ class RemoteFileBrowserItemsViewModel @Inject constructor( val currentPath: LiveData get() = _currentPath + private val _selectedPaths: MutableLiveData> = MutableLiveData(emptySet()) + val selectedPaths: LiveData> + get() = _selectedPaths + init { appPreferences.registerSortingChangeListener(sortingPrefListener) } @@ -144,6 +162,65 @@ class RemoteFileBrowserItemsViewModel @Inject constructor( } } + fun onSelectionDone() { + val selection = selectedPaths.value + if (!selection.isNullOrEmpty()) { + _viewState.value = FinishState(selection) + } + } + + fun onItemClicked(remoteFileBrowserItem: RemoteFileBrowserItem) { + if (remoteFileBrowserItem.mimeType == MIME_DIRECTORY) { + changePath(remoteFileBrowserItem.path!!) + } else { + toggleBrowserItemSelection(remoteFileBrowserItem.path!!) + } + } + + private fun toggleBrowserItemSelection(path: String) { + val paths = selectedPaths.value!!.toMutableSet() + if (paths.contains(path) || shouldPathBeSelectedDueToParent(path)) { + checkAndRemoveAnySelectedParents(path) + } else { + // TODO if it's a folder, remove all the children we added manually + paths.add(path) + _selectedPaths.value = paths + } + } + + private fun checkAndRemoveAnySelectedParents(currentPath: String) { + var file = File(currentPath) + val paths = selectedPaths.value!!.toMutableSet() + paths.remove(currentPath) + while (file.parent != null) { + paths.remove(file.parent!! + File.pathSeparator) + file = File(file.parent!!) + } + _selectedPaths.value = paths + } + + private fun shouldPathBeSelectedDueToParent(currentPath: String): Boolean { + var file = File(currentPath) + val paths = selectedPaths.value!! + if (paths.isNotEmpty() && file.parent != ROOT_PATH) { + while (file.parent != null) { + var parent = file.parent!! + if (File(file.parent!!).parent != null) { + parent += File.pathSeparator + } + if (paths.contains(parent)) { + return true + } + file = File(file.parent!!) + } + } + return false + } + + fun isPathSelected(path: String): Boolean { + return selectedPaths.value?.contains(path) == true || shouldPathBeSelectedDueToParent(path) + } + companion object { private val TAG = RemoteFileBrowserItemsViewModel::class.simpleName private const val ROOT_PATH = "/"