diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 94e2829c..457a4c65 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -143,6 +143,7 @@ dependencies {
implementation(libs.kotlinx.immutable.collections)
implementation(libs.truetype.parser)
implementation(libs.fsaf)
+ implementation(libs.storage.util)
}
detekt {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6b57937e..fa8268ec 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,11 @@
xmlns:tools="http://schemas.android.com/tools">
+
+
()
val context = LocalContext.current
Scaffold(
topBar = {
@@ -80,34 +79,75 @@ data class FilePickerScreen(val uri: String) : Screen() {
)
},
) { paddingValues ->
- FilePicker(
- directory = fileManager.fromUri(uri.toUri())!!,
- onNavigate = { newFile ->
- if (fileManager.isFile(newFile)) {
- HomeScreen.playFile(newFile.getFullPath(), context)
- return@FilePicker
- }
- navigator.push(FilePickerScreen(newFile.getFullPath()))
- },
- modifier = Modifier
- .fillMaxSize()
- .padding(paddingValues),
- )
+ if (path == null) {
+ StoragePicker(
+ onNavigate = { device -> navigator.push(FilePickerScreen(device.absolutePath)) },
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues),
+ )
+ } else {
+ FilePicker(
+ directory = File(path),
+ onNavigate = { newFile ->
+ if (newFile.isFile) {
+ HomeScreen.playFile(Uri.fromFile(newFile).toString(), context)
+ return@FilePicker
+ }
+ navigator.push(FilePickerScreen(newFile.absolutePath))
+ },
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues),
+ )
+ }
+ }
+ }
+
+ @Composable
+ fun StoragePicker(
+ modifier: Modifier = Modifier,
+ onNavigate: (File) -> Unit,
+ ) {
+ val context = LocalContext.current
+ val deviceList = StorageUtil.getStorageDirectories(context)
+ .map { File(it) }
+ .filter { it.exists() }
+
+ LazyColumn(modifier) {
+ itemsIndexed(deviceList, key = { _, file -> file.absolutePath }) { index, file ->
+ FileListing(
+ name = file.absolutePath,
+ isDirectory = true,
+ lastModified = null,
+ length = null,
+ modifier = Modifier.background(
+ if (index % 2 == 0) {
+ MaterialTheme.colorScheme.surfaceContainerLow
+ } else {
+ MaterialTheme.colorScheme.surfaceContainerHigh
+ },
+ ),
+ items = null,
+ onClick = { onNavigate(file) },
+ )
+ }
}
}
@Composable
fun FilePicker(
- directory: AbstractFile,
+ directory: File,
modifier: Modifier = Modifier,
- onNavigate: (AbstractFile) -> Unit,
+ onNavigate: (File) -> Unit,
) {
val navigator = LocalNavigator.currentOrThrow
- val fileManager = koinInject()
- val fileList = fileManager.listFiles(directory).filterNot {
- !Utils.MEDIA_EXTENSIONS.contains(fileManager.getName(it).substringAfterLast('.')) &&
- fileManager.isFile(it) || fileManager.getName(it).startsWith('.')
- }.sortedWith(FilesComparator(fileManager))
+ val fileList = directory.listFiles { file ->
+ val name = file.name
+ if (name.startsWith('.')) return@listFiles false
+ if (file.isDirectory) return@listFiles true
+ file.isFile && Utils.MEDIA_EXTENSIONS.contains(name.substringAfterLast('.').lowercase(Locale.ENGLISH))
+ }?.sortedWith(FilesComparator()) ?: emptyList()
LazyColumn(modifier) {
item {
@@ -120,12 +160,12 @@ data class FilePickerScreen(val uri: String) : Screen() {
modifier = Modifier.background(MaterialTheme.colorScheme.surfaceContainerLow),
)
}
- itemsIndexed(fileList, key = { _, file -> fileManager.getName(file) }) { index, file ->
+ itemsIndexed(fileList, key = { _, file -> file.name }) { index, file ->
FileListing(
- name = fileManager.getName(file),
- isDirectory = fileManager.isDirectory(file),
- lastModified = fileManager.lastModified(file),
- length = if (fileManager.isFile(file)) fileManager.getLength(file) else null,
+ name = file.name,
+ isDirectory = file.isDirectory,
+ lastModified = file.lastModified(),
+ length = if (file.isFile) file.length() else null,
modifier = Modifier.background(
if (index % 2 == 1) {
MaterialTheme.colorScheme.surfaceContainerLow
@@ -133,7 +173,7 @@ data class FilePickerScreen(val uri: String) : Screen() {
MaterialTheme.colorScheme.surfaceContainerHigh
},
),
- items = if (fileManager.isDirectory(file)) fileManager.listFiles(file).size else null,
+ items = if (file.isDirectory) file.listFiles()?.size else null,
onClick = { onNavigate(file) },
)
}
diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt b/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt
index 78fa0e4b..537db092 100644
--- a/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt
+++ b/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt
@@ -1,7 +1,12 @@
package live.mehiz.mpvkt.ui.home
+import android.Manifest
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Environment
+import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
@@ -11,7 +16,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.FileOpen
import androidx.compose.material.icons.filled.FolderOpen
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Link
@@ -36,10 +40,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
-import com.github.k1rakishou.fsaf.FileManager
import `is`.xyz.mpv.Utils.PROTOCOLS
import live.mehiz.mpvkt.R
import live.mehiz.mpvkt.presentation.Screen
@@ -108,31 +112,36 @@ object HomeScreen : Screen() {
Text(text = stringResource(R.string.home_open_url))
}
}
- val documentPicker = rememberLauncherForActivityResult(
- ActivityResultContracts.OpenDocument(),
- ) {
- if (it == null) return@rememberLauncherForActivityResult
- playFile(it.toString(), context)
- }
- OutlinedButton(
- onClick = { documentPicker.launch(arrayOf("*/*")) },
- ) {
- Row(
- horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.smaller),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(Icons.Default.FileOpen, null)
- Text(text = stringResource(R.string.home_pick_file))
+ val manageStoragePermission = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) {
+ navigator.push(FilePickerScreen())
}
}
- val fileManager = FileManager(context)
- val directoryPicker = rememberLauncherForActivityResult(
- ActivityResultContracts.OpenDocumentTree(),
- ) {
- if (it == null) return@rememberLauncherForActivityResult
- navigator.push(FilePickerScreen(fileManager.fromUri(it)!!.getFullPath()))
+ val readStoragePermission = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
+ if (isGranted) {
+ navigator.push(FilePickerScreen())
+ }
}
- OutlinedButton(onClick = { directoryPicker.launch(null) }) {
+ OutlinedButton(onClick = {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (!Environment.isExternalStorageManager()) {
+ manageStoragePermission.launch(
+ Intent(
+ Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
+ "package:${context.packageName}".toUri(),
+ )
+ )
+ } else {
+ navigator.push(FilePickerScreen())
+ }
+ } else {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ readStoragePermission.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
+ } else {
+ navigator.push(FilePickerScreen())
+ }
+ }
+ }) {
Row(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.smaller),
verticalAlignment = Alignment.CenterVertically,
diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/utils/FilesComparator.kt b/app/src/main/java/live/mehiz/mpvkt/ui/utils/FilesComparator.kt
index f7e2a852..74433ac5 100644
--- a/app/src/main/java/live/mehiz/mpvkt/ui/utils/FilesComparator.kt
+++ b/app/src/main/java/live/mehiz/mpvkt/ui/utils/FilesComparator.kt
@@ -1,19 +1,16 @@
package live.mehiz.mpvkt.ui.utils
-import com.github.k1rakishou.fsaf.FileManager
-import com.github.k1rakishou.fsaf.file.AbstractFile
+import java.io.File
/**
* Sorts files/directories alphabetically while giving directories priority
* credit goes to mpv-android
*/
-class FilesComparator(
- private val fileManager: FileManager
-) : Comparator {
- override fun compare(o1: AbstractFile?, o2: AbstractFile?): Int {
- val iso1ADirectory = fileManager.isDirectory(o1!!)
- val iso2ADirectory = fileManager.isDirectory(o2!!)
+class FilesComparator : Comparator {
+ override fun compare(o1: File?, o2: File?): Int {
+ val iso1ADirectory = o1!!.isDirectory
+ val iso2ADirectory = o2!!.isDirectory
if (iso1ADirectory != iso2ADirectory) return if (iso2ADirectory) 1 else -1
- return fileManager.getName(o1).compareTo(fileManager.getName(o2), ignoreCase = true)
+ return o1.name.compareTo(o2.name, ignoreCase = true)
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 27d351ee..3d452630 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -57,6 +57,7 @@ kotlinx-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collec
truetype-parser = { module = "io.github.yubyf:truetypeparser-light", version = "2.1.4" }
fsaf = { module = "com.github.K1rakishou:Fuck-Storage-Access-Framework", version = "1.1.3" }
+storage-util = { module = "com.github.hendrawd:StorageUtil", version = "1.1.0" }
about-libs-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "about-libs" }
about-libs-ui-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "about-libs" }