From 8d745b0ea24be68d6ffe9ef3a86131f004aeda34 Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Tue, 24 Mar 2026 23:48:05 +0530 Subject: [PATCH 1/2] Support secondary storage(s) in folder picker --- .../co/adityarajput/fileflow/utils/Files.kt | 43 +++++++++++++++++++ .../components/FolderPickerBottomSheet.kt | 29 +++++++++++-- app/src/main/res/drawable/folder_switch.xml | 10 +++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/drawable/folder_switch.xml diff --git a/app/src/main/java/co/adityarajput/fileflow/utils/Files.kt b/app/src/main/java/co/adityarajput/fileflow/utils/Files.kt index c8e6e46..d5a2ab1 100644 --- a/app/src/main/java/co/adityarajput/fileflow/utils/Files.kt +++ b/app/src/main/java/co/adityarajput/fileflow/utils/Files.kt @@ -1,6 +1,9 @@ package co.adityarajput.fileflow.utils import android.content.Context +import android.os.Build +import android.os.Environment +import android.os.storage.StorageManager import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile import co.adityarajput.fileflow.data.models.Rule @@ -205,3 +208,43 @@ fun String.getGetDirectoryFromUri() = fun Context.findRulesToBeMigrated(rules: List) = rules.filter { File.fromPath(this, it.action.src) == null } + +fun Context.getAllStorages(): List { + val storages = mutableListOf() + + try { + getExternalFilesDirs(null).forEach { + var storage = it + var dir = it + while (dir.parentFile != null) { + dir = dir.parentFile!! + if (dir.canRead()) + storage = dir + } + storages.add(storage) + } + } catch (e: Exception) { + Logger.e("Files", "Couldn't extract storages from external app directories", e) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + try { + (getSystemService(Context.STORAGE_SERVICE) as StorageManager).getStorageVolumes() + .forEach { + if (it.directory != null && it.directory!!.canRead()) { + storages.add(it.directory!!) + } + } + } catch (e: Exception) { + Logger.e("Files", "Couldn't extract storages from StorageManager", e) + } + } + + Logger.d("Files", "Found storages: ${storages.joinToString { it.absolutePath }}") + + return storages.distinct().apply { + val primaryStorage = Environment.getExternalStorageDirectory() + storages.remove(primaryStorage) + storages.add(0, primaryStorage) + } +} diff --git a/app/src/main/java/co/adityarajput/fileflow/views/components/FolderPickerBottomSheet.kt b/app/src/main/java/co/adityarajput/fileflow/views/components/FolderPickerBottomSheet.kt index b0c2bfe..bb5513d 100644 --- a/app/src/main/java/co/adityarajput/fileflow/views/components/FolderPickerBottomSheet.kt +++ b/app/src/main/java/co/adityarajput/fileflow/views/components/FolderPickerBottomSheet.kt @@ -1,6 +1,5 @@ package co.adityarajput.fileflow.views.components -import android.os.Environment import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row @@ -21,6 +20,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import co.adityarajput.fileflow.R +import co.adityarajput.fileflow.utils.getAllStorages import co.adityarajput.fileflow.viewmodels.UpsertRuleViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -28,7 +28,11 @@ import co.adityarajput.fileflow.viewmodels.UpsertRuleViewModel fun FolderPickerBottomSheet(viewModel: UpsertRuleViewModel) { val context = LocalContext.current val hideSheet = { viewModel.folderPickerState = null } - var currentDir by remember { mutableStateOf(Environment.getExternalStorageDirectory()) } + + val storages = remember { context.getAllStorages() } + var currentStorage by remember { mutableIntStateOf(0) } + + var currentDir by remember { mutableStateOf(storages[0]) } val items = remember(currentDir) { currentDir.listFiles()?.sortedBy { it.name.lowercase() }?.sortedBy { it.isFile }.orEmpty() } @@ -54,12 +58,31 @@ fun FolderPickerBottomSheet(viewModel: UpsertRuleViewModel) { .padding(dimensionResource(R.dimen.padding_medium)), verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)), ) { + if (currentDir in storages) { + item { + Row( + Modifier + .fillMaxWidth() + .clickable { + currentStorage = (currentStorage + 1) % storages.size + currentDir = storages[currentStorage] + }, + Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), + ) { + Icon( + painterResource(R.drawable.folder_switch), + stringResource(R.string.storage_switch), + ) + Text(stringResource(R.string.storage_switch)) + } + } + } if (currentDir.parentFile?.canRead() ?: false) { item { Row( Modifier .fillMaxWidth() - .clickable { currentDir = currentDir.parentFile }, + .clickable { currentDir = currentDir.parentFile!! }, Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)), ) { Icon( diff --git a/app/src/main/res/drawable/folder_switch.xml b/app/src/main/res/drawable/folder_switch.xml new file mode 100644 index 0000000..b16ddc4 --- /dev/null +++ b/app/src/main/res/drawable/folder_switch.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6fc05f8..858953f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -124,6 +124,7 @@ Delete if unmodified for a while + Switch storage Back (Empty) Folder From 6ca0eac02beef92fbfde803d0013190f9aaa4e5c Mon Sep 17 00:00:00 2001 From: Aditya Rajput Date: Wed, 25 Mar 2026 09:57:10 +0530 Subject: [PATCH 2/2] Update version --- app/build.gradle.kts | 4 ++-- app/src/main/res/values/strings.xml | 2 +- metadata/en-US/changelogs/5.txt | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 metadata/en-US/changelogs/5.txt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index df3f715..0a71417 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { applicationId = "co.adityarajput.fileflow" minSdk = 29 targetSdk = 36 - versionCode = 4 - versionName = "1.2.0" + versionCode = 5 + versionName = "1.3.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 858953f..249bd3f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ FileFlow FileFlow - 1.2.0 + 1.3.0 About diff --git a/metadata/en-US/changelogs/5.txt b/metadata/en-US/changelogs/5.txt new file mode 100644 index 0000000..205bf59 --- /dev/null +++ b/metadata/en-US/changelogs/5.txt @@ -0,0 +1,3 @@ +• feat: Add a special reference for UUID replacement (check project wiki for details) +• feat: Provide "don't rename" template shortcut in supporting text +• feat: Support secondary storage(s) in folder picker