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/ArkFilePickerViewModel.kt b/filepicker/src/main/java/dev/arkbuilders/components/filepicker/ArkFilePickerViewModel.kt index cafe146..a065a0d 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 @@ -191,9 +192,8 @@ 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.hasNestedOrParentalRoot(roots) + val hasNestedRoot = file.hasNestedRoot(roots) if (hasNestedRoot) { postSideEffect(FilePickerSideEffect.NestedRootProhibited) @@ -203,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) 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