Skip to content

Commit 696b099

Browse files
authored
feat: Support secondary storage(s) in folder picker (#19)
2 parents f5948d0 + 6ca0eac commit 696b099

File tree

6 files changed

+86
-6
lines changed

6 files changed

+86
-6
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ android {
2222
applicationId = "co.adityarajput.fileflow"
2323
minSdk = 29
2424
targetSdk = 36
25-
versionCode = 4
26-
versionName = "1.2.0"
25+
versionCode = 5
26+
versionName = "1.3.0"
2727

2828
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2929
}

app/src/main/java/co/adityarajput/fileflow/utils/Files.kt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package co.adityarajput.fileflow.utils
22

33
import android.content.Context
4+
import android.os.Build
5+
import android.os.Environment
6+
import android.os.storage.StorageManager
47
import androidx.core.net.toUri
58
import androidx.documentfile.provider.DocumentFile
69
import co.adityarajput.fileflow.data.models.Rule
@@ -205,3 +208,43 @@ fun String.getGetDirectoryFromUri() =
205208

206209
fun Context.findRulesToBeMigrated(rules: List<Rule>) =
207210
rules.filter { File.fromPath(this, it.action.src) == null }
211+
212+
fun Context.getAllStorages(): List<IOFile> {
213+
val storages = mutableListOf<IOFile>()
214+
215+
try {
216+
getExternalFilesDirs(null).forEach {
217+
var storage = it
218+
var dir = it
219+
while (dir.parentFile != null) {
220+
dir = dir.parentFile!!
221+
if (dir.canRead())
222+
storage = dir
223+
}
224+
storages.add(storage)
225+
}
226+
} catch (e: Exception) {
227+
Logger.e("Files", "Couldn't extract storages from external app directories", e)
228+
}
229+
230+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
231+
try {
232+
(getSystemService(Context.STORAGE_SERVICE) as StorageManager).getStorageVolumes()
233+
.forEach {
234+
if (it.directory != null && it.directory!!.canRead()) {
235+
storages.add(it.directory!!)
236+
}
237+
}
238+
} catch (e: Exception) {
239+
Logger.e("Files", "Couldn't extract storages from StorageManager", e)
240+
}
241+
}
242+
243+
Logger.d("Files", "Found storages: ${storages.joinToString { it.absolutePath }}")
244+
245+
return storages.distinct().apply {
246+
val primaryStorage = Environment.getExternalStorageDirectory()
247+
storages.remove(primaryStorage)
248+
storages.add(0, primaryStorage)
249+
}
250+
}

app/src/main/java/co/adityarajput/fileflow/views/components/FolderPickerBottomSheet.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package co.adityarajput.fileflow.views.components
22

3-
import android.os.Environment
43
import androidx.compose.foundation.clickable
54
import androidx.compose.foundation.layout.Arrangement
65
import androidx.compose.foundation.layout.Row
@@ -21,14 +20,19 @@ import androidx.compose.ui.text.font.FontWeight
2120
import androidx.compose.ui.text.style.TextAlign
2221
import androidx.compose.ui.text.style.TextOverflow
2322
import co.adityarajput.fileflow.R
23+
import co.adityarajput.fileflow.utils.getAllStorages
2424
import co.adityarajput.fileflow.viewmodels.UpsertRuleViewModel
2525

2626
@OptIn(ExperimentalMaterial3Api::class)
2727
@Composable
2828
fun FolderPickerBottomSheet(viewModel: UpsertRuleViewModel) {
2929
val context = LocalContext.current
3030
val hideSheet = { viewModel.folderPickerState = null }
31-
var currentDir by remember { mutableStateOf(Environment.getExternalStorageDirectory()) }
31+
32+
val storages = remember { context.getAllStorages() }
33+
var currentStorage by remember { mutableIntStateOf(0) }
34+
35+
var currentDir by remember { mutableStateOf(storages[0]) }
3236
val items = remember(currentDir) {
3337
currentDir.listFiles()?.sortedBy { it.name.lowercase() }?.sortedBy { it.isFile }.orEmpty()
3438
}
@@ -54,12 +58,31 @@ fun FolderPickerBottomSheet(viewModel: UpsertRuleViewModel) {
5458
.padding(dimensionResource(R.dimen.padding_medium)),
5559
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)),
5660
) {
61+
if (currentDir in storages) {
62+
item {
63+
Row(
64+
Modifier
65+
.fillMaxWidth()
66+
.clickable {
67+
currentStorage = (currentStorage + 1) % storages.size
68+
currentDir = storages[currentStorage]
69+
},
70+
Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)),
71+
) {
72+
Icon(
73+
painterResource(R.drawable.folder_switch),
74+
stringResource(R.string.storage_switch),
75+
)
76+
Text(stringResource(R.string.storage_switch))
77+
}
78+
}
79+
}
5780
if (currentDir.parentFile?.canRead() ?: false) {
5881
item {
5982
Row(
6083
Modifier
6184
.fillMaxWidth()
62-
.clickable { currentDir = currentDir.parentFile },
85+
.clickable { currentDir = currentDir.parentFile!! },
6386
Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)),
6487
) {
6588
Icon(
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:tint="?attr/colorControlNormal"
5+
android:viewportWidth="960"
6+
android:viewportHeight="960">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M360,776Q279,744 226.5,675.5Q174,607 163,520L244,520Q253,573 283,617Q313,661 360,688L360,776ZM500,880Q475,880 457.5,862.5Q440,845 440,820L440,580Q440,555 457.5,537.5Q475,520 500,520L588,520Q603,520 616.5,527Q630,534 638,547L660,580L820,580Q845,580 862.5,597.5Q880,615 880,640L880,820Q880,845 862.5,862.5Q845,880 820,880L500,880ZM140,440Q115,440 97.5,422.5Q80,405 80,380L80,140Q80,115 97.5,97.5Q115,80 140,80L228,80Q243,80 256.5,87Q270,94 278,107L300,140L460,140Q485,140 502.5,157.5Q520,175 520,200L520,380Q520,405 502.5,422.5Q485,440 460,440L140,440ZM720,480Q720,415 688,359.5Q656,304 600,272L600,184Q691,221 745.5,301.5Q800,382 800,480L720,480ZM520,800L800,800Q800,800 800,800Q800,800 800,800L800,660Q800,660 800,660Q800,660 800,660L617,660L577,600Q577,600 577,600Q577,600 577,600L520,600Q520,600 520,600Q520,600 520,600L520,800Q520,800 520,800Q520,800 520,800ZM160,360L440,360Q440,360 440,360Q440,360 440,360L440,220Q440,220 440,220Q440,220 440,220L257,220L217,160Q217,160 217,160Q217,160 217,160L160,160Q160,160 160,160Q160,160 160,160L160,360Q160,360 160,360Q160,360 160,360ZM520,800Q520,800 520,800Q520,800 520,800L520,600Q520,600 520,600Q520,600 520,600L520,600Q520,600 520,600Q520,600 520,600L520,660L520,660Q520,660 520,660Q520,660 520,660L520,800Q520,800 520,800Q520,800 520,800ZM160,360Q160,360 160,360Q160,360 160,360L160,160Q160,160 160,160Q160,160 160,160L160,160Q160,160 160,160Q160,160 160,160L160,220L160,220Q160,220 160,220Q160,220 160,220L160,360Q160,360 160,360Q160,360 160,360Z" />
10+
</vector>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<resources>
22
<string name="app_name" translatable="false">FileFlow</string>
33
<string name="app_name_launcher" translatable="false">FileFlow</string>
4-
<string name="app_version" translatable="false">1.2.0</string>
4+
<string name="app_version" translatable="false">1.3.0</string>
55

66
<!-- region AboutScreen -->
77
<string name="about">About</string>
@@ -124,6 +124,7 @@
124124
<string name="delete_stale_phrase">Delete if unmodified for a while</string>
125125
<!-- endregion -->
126126
<!-- region FolderPickerBottomSheet -->
127+
<string name="storage_switch">Switch storage</string>
127128
<string name="parent_directory">Back</string>
128129
<string name="empty_folder">(Empty)</string>
129130
<string name="folder">Folder</string>

metadata/en-US/changelogs/5.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
• feat: Add a special reference for UUID replacement (check project wiki for details)
2+
• feat: Provide "don't rename" template shortcut in supporting text
3+
• feat: Support secondary storage(s) in folder picker

0 commit comments

Comments
 (0)