From 99792cd42205e7446bbbf259da74d0f6f7418707 Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sun, 1 Dec 2024 13:08:08 +0600 Subject: [PATCH 1/6] Move RootPicker to filepicker module --- .../filepicker/ArkFilePickerConfig.kt | 2 +- .../filepicker/ArkRootPickerFragment.kt | 97 +++++++++++++++++++ .../filepicker/FragmentManagerUtils.kt | 17 ++++ filepicker/src/main/res/values/strings.xml | 2 + .../dev/arkbuilders/sample/MainActivity.kt | 3 +- .../arkbuilders/sample/RootFavPickerDialog.kt | 58 ----------- .../arkbuilders/components/utils/PathExt.kt | 9 +- 7 files changed, 127 insertions(+), 61 deletions(-) create mode 100644 filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt delete mode 100644 sample/src/main/java/dev/arkbuilders/sample/RootFavPickerDialog.kt diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerConfig.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerConfig.kt index ed01069..b0719e8 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerConfig.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerConfig.kt @@ -6,7 +6,7 @@ import androidx.annotation.StyleRes import dev.arkbuilders.components.filepicker.R import java.nio.file.Path -class ArkFilePickerConfig( +data class ArkFilePickerConfig( @StringRes val titleStringId: Int = R.string.ark_file_picker_pick_title, @StringRes diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt new file mode 100644 index 0000000..a01ccc6 --- /dev/null +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt @@ -0,0 +1,97 @@ +package dev.arkbuilders.components.filepicker + +import androidx.core.os.bundleOf +import androidx.fragment.app.setFragmentResult +import androidx.lifecycle.lifecycleScope +import dev.arkbuilders.arklib.data.folders.FoldersRepo +import dev.arkbuilders.components.utils.hasNestedRoot +import dev.arkbuilders.components.utils.isInternalStorage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.nio.file.Path + +class ArkRootPickerFragment : ArkFilePickerFragment() { + private var rootNotFavorite = false + + override fun onFolderChanged(currentFolder: Path) { + lifecycleScope.launch { + val folders = FoldersRepo.instance.provideFolders() + val roots = folders.keys + + if (currentFolder.isInternalStorage() || currentFolder.hasNestedRoot(roots)) { + rootNotFavorite = true + binding.btnPick.text = getString(R.string.ark_file_picker_root) + binding.btnPick.isEnabled = false + return@launch + } + + val root = roots.find { root -> currentFolder.startsWith(root) } + root?.let { + if (root == currentFolder) { + rootNotFavorite = true + binding.btnPick.text = getString(R.string.ark_file_picker_root) + binding.btnPick.isEnabled = false + } else { + val favorites = folders.map { (root, relativeFavorites) -> + relativeFavorites.map { + root.resolve(it) + } + }.flatten() + val foundAsFavorite = favorites.any { it == currentFolder } + rootNotFavorite = false + binding.btnPick.text = getString(R.string.ark_file_picker_favorite) + binding.btnPick.isEnabled = !foundAsFavorite + } + } ?: let { + rootNotFavorite = true + binding.btnPick.text = getString(R.string.ark_file_picker_root) + binding.btnPick.isEnabled = true + } + } + } + + override fun onPick(pickedPath: Path) { + lifecycleScope.launch(Dispatchers.IO) { + addRootOrFavorite(pickedPath) + setFragmentResult( + ROOT_PICKED_REQUEST_KEY, + bundleOf().apply { + putString(PICKED_PATH_BUNDLE_KEY, pickedPath.toString()) + putBoolean(ROOT_NOT_FAV_BUNDLE_KEY, rootNotFavorite) + } + ) + } + } + + private suspend fun addRootOrFavorite(pickedPath: Path) { + val folders = FoldersRepo.instance.provideFolders() + if (rootNotFavorite) { + FoldersRepo.instance.addRoot(pickedPath) + } else { + val root = folders.keys.find { pickedPath.startsWith(it) } + ?: throw IllegalStateException( + "Can't add favorite if it's root is not added" + ) + val favoriteRelativePath = root.relativize(pickedPath) + FoldersRepo.instance.addFavorite(root, favoriteRelativePath) + } + } + + companion object { + const val ROOT_PICKED_REQUEST_KEY = "rootPicked" + const val PICKED_PATH_BUNDLE_KEY = "pickedPath" + const val ROOT_NOT_FAV_BUNDLE_KEY = "rootNotFav" + + fun newInstance( + config: ArkFilePickerConfig = ArkFilePickerConfig() + ) = ArkRootPickerFragment().apply { + setConfig( + config.copy( + showRoots = false, + mode = ArkFilePickerMode.FOLDER, + pathPickedRequestKey = "notUsed" + ) + ) + } + } +} \ No newline at end of file diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/FragmentManagerUtils.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/FragmentManagerUtils.kt index a198434..5e451e1 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/FragmentManagerUtils.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/FragmentManagerUtils.kt @@ -20,4 +20,21 @@ fun FragmentManager.onArkPathPicked( ) ) } +} + +fun FragmentManager.onArkRootOrFavPicked( + lifecycleOwner: LifecycleOwner, + listener: (path: Path, rootNotFavorite: Boolean) -> Unit +) { + setFragmentResultListener( + ArkRootPickerFragment.ROOT_PICKED_REQUEST_KEY, + lifecycleOwner + ) { _, bundle -> + listener( + Path( + bundle.getString(ArkRootPickerFragment.PICKED_PATH_BUNDLE_KEY)!! + ), + bundle.getBoolean(ArkRootPickerFragment.ROOT_NOT_FAV_BUNDLE_KEY) + ) + } } \ No newline at end of file diff --git a/filepicker/src/main/res/values/strings.xml b/filepicker/src/main/res/values/strings.xml index 20116db..3ba7705 100644 --- a/filepicker/src/main/res/values/strings.xml +++ b/filepicker/src/main/res/values/strings.xml @@ -18,6 +18,8 @@ Only folder can be pinned. There\'s already nested root(s) inside. Pin + Root + Favorite %d item %d items diff --git a/sample/src/main/java/dev/arkbuilders/sample/MainActivity.kt b/sample/src/main/java/dev/arkbuilders/sample/MainActivity.kt index db201bd..cef3940 100644 --- a/sample/src/main/java/dev/arkbuilders/sample/MainActivity.kt +++ b/sample/src/main/java/dev/arkbuilders/sample/MainActivity.kt @@ -16,6 +16,7 @@ import com.google.android.material.button.MaterialButton import dev.arkbuilders.components.filepicker.ArkFilePickerConfig import dev.arkbuilders.components.filepicker.ArkFilePickerFragment import dev.arkbuilders.components.filepicker.ArkFilePickerMode +import dev.arkbuilders.components.filepicker.ArkRootPickerFragment import dev.arkbuilders.components.filepicker.onArkPathPicked import dev.arkbuilders.sample.about.AboutActivity import dev.arkbuilders.sample.storage.StorageDemoFragment @@ -39,7 +40,7 @@ class MainActivity : AppCompatActivity() { findViewById(R.id.btn_root_picker).setOnClickListener { resolvePermissions() - RootFavPickerDialog + ArkRootPickerFragment .newInstance() .show(supportFragmentManager, null) } diff --git a/sample/src/main/java/dev/arkbuilders/sample/RootFavPickerDialog.kt b/sample/src/main/java/dev/arkbuilders/sample/RootFavPickerDialog.kt deleted file mode 100644 index bea52c3..0000000 --- a/sample/src/main/java/dev/arkbuilders/sample/RootFavPickerDialog.kt +++ /dev/null @@ -1,58 +0,0 @@ -package dev.arkbuilders.sample - -import android.widget.Toast -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.launch -import dev.arkbuilders.arklib.data.folders.FoldersRepo -import dev.arkbuilders.components.filepicker.ArkFilePickerConfig -import dev.arkbuilders.components.filepicker.ArkFilePickerFragment -import java.nio.file.Path - -class RootFavPickerDialog : ArkFilePickerFragment() { - var rootNotFavorite = false - - override fun onFolderChanged(currentFolder: Path) { - lifecycleScope.launch { - val folders = FoldersRepo.instance.provideFolders() - val roots = folders.keys - val favorites = folders.values.flatten() - val root = roots.find { root -> currentFolder.startsWith(root) } - root?.let { - if (root == currentFolder) { - rootNotFavorite = true - binding.btnPick.text = "Root" - binding.btnPick.isEnabled = false - } else { - var foundAsFavorite = false - favorites.forEach { - if (currentFolder.endsWith(it)) { - foundAsFavorite = true - return@forEach - } - } - rootNotFavorite = false - binding.btnPick.text = "Favorite" - binding.btnPick.isEnabled = !foundAsFavorite - } - } ?: let { - rootNotFavorite = true - binding.btnPick.text = "Root" - binding.btnPick.isEnabled = true - } - } - } - - override fun onPick(pickedPath: Path) { - Toast.makeText( - requireContext(), - "rootNotFavorite [$rootNotFavorite]", - Toast.LENGTH_SHORT - ).show() - } - - companion object { - fun newInstance() = RootFavPickerDialog().apply { - setConfig(ArkFilePickerConfig()) - } - } -} \ No newline at end of file diff --git a/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt b/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt index df0d9a9..69723bc 100644 --- a/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt +++ b/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt @@ -1,10 +1,17 @@ package dev.arkbuilders.components.utils import java.nio.file.Path +import kotlin.io.path.Path + +val INTERNAL_STORAGE = Path("/storage/emulated/0") fun Path.hasNestedOrParentalRoot(roots: Iterable): Boolean { val hasNestedRoot = roots.any { path -> this.startsWith(path) || path.startsWith(this) } return hasNestedRoot -} \ No newline at end of file +} + +fun Path.hasNestedRoot(roots: Iterable) = roots.any { it.startsWith(this) } + +fun Path.isInternalStorage() = this == INTERNAL_STORAGE \ No newline at end of file From 48caf42a37bd3a535a1230b360b418a01e943db4 Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sun, 1 Dec 2024 13:10:44 +0600 Subject: [PATCH 2/6] Pin: check only for nested roots (under parent root we can make fav) --- .../components/filepicker/ArkFilePickerViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt index cafe146..c5c2b04 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt @@ -15,6 +15,7 @@ import dev.arkbuilders.arklib.data.folders.FoldersRepo import dev.arkbuilders.arklib.utils.DeviceStorageUtils import dev.arkbuilders.arklib.utils.listChildren import dev.arkbuilders.components.utils.hasNestedOrParentalRoot +import dev.arkbuilders.components.utils.hasNestedRoot import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.nio.file.Path @@ -193,7 +194,7 @@ internal class ArkFilePickerViewModel( val root = roots.find { root -> file.startsWith(root) } val favorites = rootsWithFavorites[root]?.flatten() - val hasNestedRoot = file.hasNestedOrParentalRoot(roots) + val hasNestedRoot = file.hasNestedRoot(roots) if (hasNestedRoot) { postSideEffect(FilePickerSideEffect.NestedRootProhibited) From 15b624d1daa6c97dd999c6a70f2ce70c29a6e159 Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sun, 1 Dec 2024 13:14:19 +0600 Subject: [PATCH 3/6] Pin: better check for favorites (map relatives to full path) --- .../components/filepicker/ArkFilePickerViewModel.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt index c5c2b04..a065a0d 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt @@ -192,7 +192,6 @@ internal class ArkFilePickerViewModel( val rootsWithFavorites = container.stateFlow.value.rootsWithFavs val roots = rootsWithFavorites.keys val root = roots.find { root -> file.startsWith(root) } - val favorites = rootsWithFavorites[root]?.flatten() val hasNestedRoot = file.hasNestedRoot(roots) @@ -204,10 +203,14 @@ internal class ArkFilePickerViewModel( val haveRoot = haveRoot() root?.let { - //Make sure file isn't inside a root folder if (root != file) { - val foundAsFavorite = favorites?.any { file.endsWith(it) } ?: false + val favorites = rootsWithFavorites.map { (root, relativeFavorites) -> + relativeFavorites.map { + root.resolve(it) + } + }.flatten() + val foundAsFavorite = favorites.any { it == file } if (!foundAsFavorite) { addFavorite(file) From beda289ae6e32bf448d42b02f955ea315b0a34ea Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sat, 28 Dec 2024 04:55:13 +0600 Subject: [PATCH 4/6] Revert favorites check with full path, check with relatives endsWith --- .../components/filepicker/ArkFilePickerViewModel.kt | 8 ++------ .../components/filepicker/ArkRootPickerFragment.kt | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt index a065a0d..4f20fd6 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt @@ -192,6 +192,7 @@ internal class ArkFilePickerViewModel( val rootsWithFavorites = container.stateFlow.value.rootsWithFavs val roots = rootsWithFavorites.keys val root = roots.find { root -> file.startsWith(root) } + val favorites = rootsWithFavorites[root]?.flatten() val hasNestedRoot = file.hasNestedRoot(roots) @@ -205,12 +206,7 @@ internal class ArkFilePickerViewModel( root?.let { //Make sure file isn't inside a root folder if (root != file) { - val favorites = rootsWithFavorites.map { (root, relativeFavorites) -> - relativeFavorites.map { - root.resolve(it) - } - }.flatten() - val foundAsFavorite = favorites.any { it == file } + val foundAsFavorite = favorites?.any { file.endsWith(it) } ?: false if (!foundAsFavorite) { addFavorite(file) diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt index a01ccc6..aa41b35 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt @@ -26,18 +26,14 @@ class ArkRootPickerFragment : ArkFilePickerFragment() { } val root = roots.find { root -> currentFolder.startsWith(root) } + val favorites = folders[root]?.flatten() root?.let { if (root == currentFolder) { rootNotFavorite = true binding.btnPick.text = getString(R.string.ark_file_picker_root) binding.btnPick.isEnabled = false } else { - val favorites = folders.map { (root, relativeFavorites) -> - relativeFavorites.map { - root.resolve(it) - } - }.flatten() - val foundAsFavorite = favorites.any { it == currentFolder } + val foundAsFavorite = favorites?.any { currentFolder.endsWith(it) } ?: false rootNotFavorite = false binding.btnPick.text = getString(R.string.ark_file_picker_favorite) binding.btnPick.isEnabled = !foundAsFavorite From c17719b4fc2007086cab35cbe4df01aa54f6e2f4 Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sat, 28 Dec 2024 04:57:35 +0600 Subject: [PATCH 5/6] ArkFilePickerViewModel: pinFile -> pinFolder --- .../components/filepicker/ArkFilePickerFragment.kt | 5 ++--- .../components/filepicker/ArkFilePickerViewModel.kt | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt index 27166dd..c6d1862 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerFragment.kt @@ -41,7 +41,6 @@ import com.mikepenz.fastadapter.binding.AbstractBindingItem import dev.arkbuilders.arklib.utils.DeviceStorageUtils import dev.arkbuilders.arklib.utils.INTERNAL_STORAGE import org.orbitmvi.orbit.viewmodel.observe -import dev.arkbuilders.components.filepicker.R import dev.arkbuilders.components.filepicker.databinding.ArkFilePickerDialogNewFolderBinding import dev.arkbuilders.components.filepicker.databinding.ArkFilePickerHostFragmentBinding import dev.arkbuilders.components.filepicker.databinding.ArkFilePickerItemFileBinding @@ -211,7 +210,7 @@ open class ArkFilePickerFragment : if (newFolder.mkdirs()) { if (isARKMode()) { - viewModel.pinFile(newFolder.toPath()) + viewModel.pinFolder(newFolder.toPath()) } //Reload current files tree currentFolder?.let { viewModel.onItemClick(it) } @@ -486,7 +485,7 @@ internal class FilesPage( val popupMenu = PopupMenu(fragment.activity, anchor, Gravity.END) popupMenu.menuInflater.inflate(R.menu.file_select_menu, popupMenu.menu) popupMenu.setOnMenuItemClickListener { - viewModel.pinFile(file) + viewModel.pinFolder(file) true } popupMenu.show() diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt index 4f20fd6..797582c 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt @@ -14,7 +14,6 @@ import org.orbitmvi.orbit.viewmodel.container import dev.arkbuilders.arklib.data.folders.FoldersRepo import dev.arkbuilders.arklib.utils.DeviceStorageUtils import dev.arkbuilders.arklib.utils.listChildren -import dev.arkbuilders.components.utils.hasNestedOrParentalRoot import dev.arkbuilders.components.utils.hasNestedRoot import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -183,7 +182,7 @@ internal class ArkFilePickerViewModel( return arkGlobal?.exists() == true } - fun pinFile(file: Path) = intent { + fun pinFolder(file: Path) = intent { if (!file.isDirectory()) { postSideEffect(FilePickerSideEffect.CannotPinFile) return@intent From 5f98d2d3de8f118b5e938c75368393912f13256f Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sat, 28 Dec 2024 05:00:49 +0600 Subject: [PATCH 6/6] Remove INTERNAL_STORAGE const from utils --- .../components/filepicker/ArkRootPickerFragment.kt | 4 ++-- .../main/java/dev/arkbuilders/components/utils/PathExt.kt | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt index aa41b35..1b71dba 100644 --- a/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt +++ b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkRootPickerFragment.kt @@ -4,8 +4,8 @@ import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult import androidx.lifecycle.lifecycleScope import dev.arkbuilders.arklib.data.folders.FoldersRepo +import dev.arkbuilders.arklib.utils.INTERNAL_STORAGE import dev.arkbuilders.components.utils.hasNestedRoot -import dev.arkbuilders.components.utils.isInternalStorage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.nio.file.Path @@ -18,7 +18,7 @@ class ArkRootPickerFragment : ArkFilePickerFragment() { val folders = FoldersRepo.instance.provideFolders() val roots = folders.keys - if (currentFolder.isInternalStorage() || currentFolder.hasNestedRoot(roots)) { + if (currentFolder == INTERNAL_STORAGE || currentFolder.hasNestedRoot(roots)) { rootNotFavorite = true binding.btnPick.text = getString(R.string.ark_file_picker_root) binding.btnPick.isEnabled = false diff --git a/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt b/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt index 69723bc..232433b 100644 --- a/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt +++ b/utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt @@ -1,9 +1,6 @@ package dev.arkbuilders.components.utils import java.nio.file.Path -import kotlin.io.path.Path - -val INTERNAL_STORAGE = Path("/storage/emulated/0") fun Path.hasNestedOrParentalRoot(roots: Iterable): Boolean { val hasNestedRoot = roots.any { path -> @@ -12,6 +9,4 @@ fun Path.hasNestedOrParentalRoot(roots: Iterable): Boolean { return hasNestedRoot } -fun Path.hasNestedRoot(roots: Iterable) = roots.any { it.startsWith(this) } - -fun Path.isInternalStorage() = this == INTERNAL_STORAGE \ No newline at end of file +fun Path.hasNestedRoot(roots: Iterable) = roots.any { it.startsWith(this) } \ No newline at end of file