From eda019a0447b9e47017fbf2fb2ea4e30115e60a2 Mon Sep 17 00:00:00 2001 From: The412Banner Date: Tue, 6 Oct 2026 23:52:59 -0400 Subject: [PATCH] File manager: up/back work from any start folder, and a ".." row to go up Opening the manager at a folder (a game's files, its Proton prefix, the saves folder, or the picker's remembered folder) ran openDrive() on the start dir, which pinned the up/back floor to that folder: the toolbar arrow was greyed out and Back closed the screen. The start dir now opens with loadDirectory(), keeping the volume-root floor computed for it, and folders in the app's private storage (internal Steam library, prefixes) floor at the app's files dir instead of themselves. Adds a ".." (Up one level) row at the top of the list and grid whenever the current folder is above its floor, sharing one goUp() with the toolbar arrow. Covers both screens built on FileManagerScreen: the File Manager and the in-app file/folder picker. Strings for all 9 locales. --- .../launcher/files/FileManagerItems.kt | 42 +++++++++++++++++++ .../launcher/files/FileManagerScreen.kt | 34 +++++++++++---- app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-zh-rCN/strings.xml | 1 + app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 11 files changed, 78 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/files/FileManagerItems.kt b/app/src/main/java/com/droiddeck/launcher/files/FileManagerItems.kt index caf3b5a71..8efe2c6cd 100644 --- a/app/src/main/java/com/droiddeck/launcher/files/FileManagerItems.kt +++ b/app/src/main/java/com/droiddeck/launcher/files/FileManagerItems.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.material.icons.filled.Checklist import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.FileCopy @@ -312,6 +313,47 @@ internal fun FileItemRow( } } +// The ".." row at the top of a folder: one level up, styled like a folder row so it reads as part +// of the list and takes controller focus the same way. +@Composable +internal fun ParentFolderRow(compact: Boolean = false, onTap: () -> Unit) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 3.dp) + .clickable(onClick = onTap), + shape = RoundedCornerShape(10.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer), + border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 12.dp, vertical = if (compact) 3.dp else 8.dp), + ) { + Icon( + imageVector = Icons.Filled.ArrowUpward, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(if (compact) 24.dp else 36.dp), + ) + Spacer(Modifier.width(10.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + text = "..", + color = MaterialTheme.colorScheme.onBackground, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + ) + Text( + text = stringResource(R.string.fm_up_one_level), + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 12.sp, + ) + } + } + } +} + // Dedicated Favorites list that replaces the file list while the star toggle is on. // Reads the store keyed on [favTick] so it recomposes after any pin/unpin. @Composable diff --git a/app/src/main/java/com/droiddeck/launcher/files/FileManagerScreen.kt b/app/src/main/java/com/droiddeck/launcher/files/FileManagerScreen.kt index be22e7d00..56556faba 100644 --- a/app/src/main/java/com/droiddeck/launcher/files/FileManagerScreen.kt +++ b/app/src/main/java/com/droiddeck/launcher/files/FileManagerScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -143,7 +144,14 @@ fun FileManagerScreen( val name = abs.removePrefix("/storage/").substringBefore('/') if (name.isNotEmpty() && name != "emulated" && name != "self") File("/storage/$name") else rootDir } - else -> rootDir + // A game's own folder or Proton prefix lives in the app's private storage, which has no + // volume above it: floor at the app's files dir so up/back still reach the library roots. + else -> { + val appFiles = runCatching { context.filesDir.canonicalPath }.getOrNull() + generateSequence(rootDir) { it.parentFile } + .firstOrNull { appFiles != null && runCatching { it.canonicalPath }.getOrNull() == appFiles } + ?: rootDir + } } mutableStateOf(vol) } @@ -232,7 +240,9 @@ fun FileManagerScreen( loadDirectory(dir) } - LaunchedEffect(Unit) { openDrive(rootDir) } + // Open the start dir without openDrive(): that pins the floor to the start dir itself, which + // greyed out up/back whenever the manager was opened at a game's folder. + LaunchedEffect(Unit) { loadDirectory(rootDir) } // System/gesture Back: while the Favorites view is open it closes that first; otherwise // it goes up one directory. Only at the current drive's root with Favorites closed is it @@ -246,6 +256,14 @@ fun FileManagerScreen( if (parent != null && parent.exists()) loadDirectory(parent) } + // One level up, never above the current drive's root: shared by the toolbar arrow and the + // ".." row at the top of the list. + val canGoUp = currentDir != currentRoot && currentDir.parentFile?.exists() == true + fun goUp() { + val parent = currentDir.parentFile + if (currentDir != currentRoot && parent != null && parent.exists()) loadDirectory(parent) + } + // Resolve a non-colliding destination in [dir] for [name] (foo.txt -> "foo (1).txt"). fun uniqueDestination(dir: File, name: String): File { var candidate = File(dir, name) @@ -617,11 +635,7 @@ fun FileManagerScreen( .background(MaterialTheme.colorScheme.surfaceContainer) .padding(horizontal = 8.dp, vertical = 6.dp), ) { - IconButton(onClick = { - val parent = currentDir.parentFile - // Don't climb above the current drive's root. - if (currentDir != currentRoot && parent != null && parent.exists()) loadDirectory(parent) - }, enabled = currentDir != currentRoot) { + IconButton(onClick = { goUp() }, enabled = canGoUp) { Icon(Icons.Filled.ArrowBack, stringResource(R.string.fm_back), tint = MaterialTheme.colorScheme.primary) } @@ -1044,6 +1058,9 @@ fun FileManagerScreen( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(8.dp), ) { + if (canGoUp) item(key = "..", span = { GridItemSpan(maxLineSpan) }) { + ParentFolderRow(compact = compactRows, onTap = { goUp() }) + } items(shownEntries, key = { it.absolutePath }) { file -> val isFav = remember(file.absolutePath, favTick) { FavoritesStore.isFavorite(context, file.absolutePath) @@ -1109,6 +1126,9 @@ fun FileManagerScreen( } } else LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) { + if (canGoUp) item(key = "..") { + ParentFolderRow(compact = compactRows, onTap = { goUp() }) + } if (entries.isEmpty()) { item { Box( diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 6e3b9e004..a17a31e67 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1567,6 +1567,7 @@ Se quitó \"%1$s\" de Favoritos Carpeta vacía + Subir un nivel Elige una carpeta Elige un archivo diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2255f73d4..0439c85ec 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -946,4 +946,5 @@ Choisir un dossier pour les fichiers du jeu frontal Fichiers du jeu enregistrés dans %1$s Fichiers frontaux + Remonter d\'un niveau \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 3102cb2ce..e22b96365 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1531,6 +1531,7 @@ 「%1$s」をお気に入りから削除しました 空のフォルダ + 1つ上の階層へ フォルダを選択 ファイルを選択 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index c6984ed04..04b781dac 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -1531,6 +1531,7 @@ \"%1$s\"을(를) 즐겨찾기에서 제거했습니다 빈 폴더 + 상위 폴더로 폴더 선택 파일 선택 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0013f3912..1504ef112 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -954,4 +954,5 @@ Выбрать папку для файлов игры фронтенда Файлы игры сохранены в %1$s Файлы фронтенда + На уровень вверх \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b8b01c90e..cb7086432 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1531,6 +1531,7 @@ 已将“%1$s”从收藏夹移除 空文件夹 + 返回上一级 选择文件夹 选择文件 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index ac9b39950..2f14bbf6d 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1531,6 +1531,7 @@ 已將「%1$s」從我的最愛移除 空白目錄 + 返回上一層 選擇資料夾 選擇檔案 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0404e6c55..52bf7f3fe 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1531,6 +1531,7 @@ 已將「%1$s」從我的最愛移除 空的資料夾 + 返回上一層 選擇資料夾 選擇檔案 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45a134090..add3b1128 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1481,6 +1481,7 @@ Removed \"%1$s\" from Favorites Empty directory + Up one level Choose a folder Choose a file