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