Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) }
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ 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
import java.nio.file.Path
Expand Down Expand Up @@ -182,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
Expand All @@ -193,7 +193,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)
Copy link
Collaborator

@tuancoltech tuancoltech Dec 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdrlzy By checking if a folder hasNestedOrParentalRoot, we are prohibiting pinning current folder if

  • it contains root(s)
    or
  • it is being contained by root(s)
    By changing from hasNestedOrParentalRoot to hasNestedRoot, we're ignoring every root folder that might contain currently folder, and still allow pining the current folder.

Is that our expectation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If folder has parental root, we should still be able to pin it as favorite. Now this is not possible, check the main branch yourself.
Sorry, my bad, I forgot about it when I was reviewing your PR


if (hasNestedRoot) {
postSideEffect(FilePickerSideEffect.NestedRootProhibited)
Expand All @@ -203,7 +203,6 @@ 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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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.arklib.utils.INTERNAL_STORAGE
import dev.arkbuilders.components.utils.hasNestedRoot
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 == INTERNAL_STORAGE || 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) }
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 foundAsFavorite = favorites?.any { currentFolder.endsWith(it) } ?: false
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"
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)!!
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of asserting the returned string value, what about giving it a non-null fallback value?
It will never crash the app even if the value cannot be found from this bundle.

Suggested change
bundle.getString(ArkRootPickerFragment.PICKED_PATH_BUNDLE_KEY)!!
bundle.getString(ArkFilePickerFragment.PATH_PICKED_PATH_BUNDLE_KEY, "")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is key for selected path from dialog. If we return fragment result bundle without selected path, then something goes terribly wrong. We want to crash in this case.

),
bundle.getBoolean(ArkRootPickerFragment.ROOT_NOT_FAV_BUNDLE_KEY)
)
}
}
2 changes: 2 additions & 0 deletions filepicker/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<string name="ark_file_picker_pin_folder_only">Only folder can be pinned.</string>
<string name="ark_file_nested_root_inside">There\'s already nested root(s) inside.</string>
<string name="ark_file_picker_pin">Pin</string>
<string name="ark_file_picker_root">Root</string>
<string name="ark_file_picker_favorite">Favorite</string>
<plurals name="ark_file_picker_items">
<item quantity="one">%d item</item>
<item quantity="other">%d items</item>
Expand Down
3 changes: 2 additions & 1 deletion sample/src/main/java/dev/arkbuilders/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -39,7 +40,7 @@ class MainActivity : AppCompatActivity() {

findViewById<MaterialButton>(R.id.btn_root_picker).setOnClickListener {
resolvePermissions()
RootFavPickerDialog
ArkRootPickerFragment
.newInstance()
.show(supportFragmentManager, null)
}
Expand Down
58 changes: 0 additions & 58 deletions sample/src/main/java/dev/arkbuilders/sample/RootFavPickerDialog.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ fun Path.hasNestedOrParentalRoot(roots: Iterable<Path>): Boolean {
this.startsWith(path) || path.startsWith(this)
}
return hasNestedRoot
}
}

fun Path.hasNestedRoot(roots: Iterable<Path>) = roots.any { it.startsWith(this) }
Loading