Skip to content
Merged
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 @@ -306,6 +306,10 @@ open class ArkFilePickerFragment :
FilePickerSideEffect.CannotPinFile -> {
activity?.toast(R.string.ark_file_picker_pin_folder_only)
}

FilePickerSideEffect.NestedRootProhibited -> {
activity?.toast(R.string.ark_file_nested_root_inside)
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.nio.file.Path
Expand Down Expand Up @@ -44,6 +45,7 @@ internal sealed class FilePickerSideEffect {
data object AlreadyFavorite : FilePickerSideEffect()
data object PinAsFirstRoot : FilePickerSideEffect()
data object CannotPinFile : FilePickerSideEffect()
data object NestedRootProhibited : FilePickerSideEffect()

}

Expand Down Expand Up @@ -190,6 +192,14 @@ internal class ArkFilePickerViewModel(
val roots = rootsWithFavorites.keys
val root = roots.find { root -> file.startsWith(root) }
val favorites = rootsWithFavorites[root]?.flatten()

val hasNestedRoot = file.hasNestedOrParentalRoot(roots)

if (hasNestedRoot) {
postSideEffect(FilePickerSideEffect.NestedRootProhibited)
return@intent
}

val haveRoot = haveRoot()

root?.let {
Expand Down
1 change: 1 addition & 0 deletions filepicker/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<string name="ark_file_picker_already_a_favorite">Already be a Favorite folder!</string>
<string name="ark_file_picker_already_be_a_root">Already be a Root folder!</string>
<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>
<plurals name="ark_file_picker_items">
<item quantity="one">%d item</item>
Expand Down
10 changes: 10 additions & 0 deletions utils/src/main/java/dev/arkbuilders/components/utils/PathExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.arkbuilders.components.utils

import java.nio.file.Path

fun Path.hasNestedOrParentalRoot(roots: Iterable<Path>): Boolean {
val hasNestedRoot = roots.any { path ->
this.startsWith(path) || path.startsWith(this)
}
return hasNestedRoot
}
132 changes: 132 additions & 0 deletions utils/src/test/java/dev/arkbuilders/components/utils/PathExtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package dev.arkbuilders.components.utils

import junit.framework.TestCase.assertEquals
import org.junit.Test
import java.nio.file.Path
import java.nio.file.Paths

class PathExtTest {

// --- Tests with Set for roots parameter ---

@Test
fun `hasNestedOrParentalRoot should return true when there is an exact match in roots`() {
val path = Paths.get("/parent/child")
val roots = setOf(Paths.get("/parent/child"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when there is a direct parent in roots`() {
val path = Paths.get("/parent/child")
val roots = setOf(Paths.get("/parent"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when there is a direct child in roots`() {
val path = Paths.get("/parent")
val roots = setOf(Paths.get("/parent/child"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when there is a nested parent in roots`() {
val path = Paths.get("/parent/child/grandchild")
val roots = setOf(Paths.get("/parent"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when there is a nested child in roots`() {
val path = Paths.get("/parent")
val roots = setOf(Paths.get("/parent/child/grandchild"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return false when there are no nested or parental roots`() {
val path = Paths.get("/parent/child")
val roots = setOf(Paths.get("/unrelated"), Paths.get("/another"))
assertEquals(path.hasNestedOrParentalRoot(roots), false)
}

@Test
fun `hasNestedOrParentalRoot should return false when roots set is empty`() {
val path = Paths.get("/parent/child")
val roots = emptySet<Path>()
assertEquals(path.hasNestedOrParentalRoot(roots), false)
}

// --- Tests with List for roots parameter ---

@Test
fun `hasNestedOrParentalRoot should return true when an exact match exists in roots (List)`() {
val path = Paths.get("/parent/child")
val roots = listOf(Paths.get("/parent/child"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when a direct parent exists in roots (List)`() {
val path = Paths.get("/parent/child")
val roots = listOf(Paths.get("/parent"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when a direct child exists in roots (List)`() {
val path = Paths.get("/parent")
val roots = listOf(Paths.get("/parent/child"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when a nested parent exists in roots (List)`() {
val path = Paths.get("/parent/child/grandchild")
val roots = listOf(Paths.get("/parent"))
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return false when there are no nested or parental roots (List)`() {
val path = Paths.get("/parent/child")
val roots = listOf(Paths.get("/unrelated"), Paths.get("/another"))
assertEquals(path.hasNestedOrParentalRoot(roots), false)
}

@Test
fun `hasNestedOrParentalRoot should return false when roots is empty`() {
val path = Paths.get("/parent/child")
val roots = emptyList<Path>()
assertEquals(path.hasNestedOrParentalRoot(roots), false)
}

@Test
fun `hasNestedOrParentalRoot should return true when duplicates of an exact match exist in roots (List)`() {
val path = Paths.get("/parent/child")
val roots = listOf(Paths.get("/parent/child"), Paths.get("/parent/child")) // Duplicate exact match
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when duplicates of a direct parent exist in roots (List)`() {
val path = Paths.get("/parent/child")
val roots = listOf(Paths.get("/parent"), Paths.get("/parent")) // Duplicate direct parent
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return true when duplicates of a nested parent exist in roots (List)`() {
val path = Paths.get("/parent/child/grandchild")
val roots = listOf(Paths.get("/parent"), Paths.get("/parent")) // Duplicate nested parent
assertEquals(path.hasNestedOrParentalRoot(roots), true)
}

@Test
fun `hasNestedOrParentalRoot should return false when duplicates of unrelated paths exist in roots (List)`() {
val path = Paths.get("/parent/child")
val roots = listOf(Paths.get("/unrelated"), Paths.get("/unrelated")) // Duplicate unrelated paths
assertEquals(path.hasNestedOrParentalRoot(roots), false)
}
}