From 9765f7a388772f867a81d51c1f8232cb902a813e Mon Sep 17 00:00:00 2001
From: Emmanuel Hansen <emmausssss@gmail.com>
Date: Sun, 12 Nov 2023 19:36:10 +0000
Subject: [PATCH] android - adjust grid view design, remove bottom app bar

---
 src/RyujinxAndroid/app/build.gradle           |   4 +-
 .../android/viewmodels/HomeViewModel.kt       |  35 ++-
 .../android/viewmodels/SettingsViewModel.kt   |  28 +++
 .../org/ryujinx/android/views/HomeViews.kt    | 238 ++++++++----------
 .../org/ryujinx/android/views/SettingViews.kt |  17 ++
 5 files changed, 161 insertions(+), 161 deletions(-)

diff --git a/src/RyujinxAndroid/app/build.gradle b/src/RyujinxAndroid/app/build.gradle
index 606440096..563360458 100644
--- a/src/RyujinxAndroid/app/build.gradle
+++ b/src/RyujinxAndroid/app/build.gradle
@@ -11,8 +11,8 @@ android {
         applicationId "org.ryujinx.android"
         minSdk 30
         targetSdk 33
-        versionCode 10004
-        versionName '1.0.4'
+        versionCode 10006
+        versionName '1.0.6'
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         vectorDrawables {
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt
index 133a500cc..8b13f903f 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt
@@ -6,9 +6,7 @@ import androidx.documentfile.provider.DocumentFile
 import androidx.preference.PreferenceManager
 import com.anggrayudi.storage.file.DocumentFileCompat
 import com.anggrayudi.storage.file.DocumentFileType
-import com.anggrayudi.storage.file.FileFullPath
 import com.anggrayudi.storage.file.extension
-import com.anggrayudi.storage.file.getAbsolutePath
 import com.anggrayudi.storage.file.search
 import org.ryujinx.android.MainActivity
 import kotlin.concurrent.thread
@@ -17,6 +15,7 @@ class HomeViewModel(
     val activity: MainActivity? = null,
     val mainViewModel: MainViewModel? = null
 ) {
+    private var savedFolder: String = ""
     private var isLoading: Boolean = false
     private var loadedCache: List<GameModel> = listOf()
     private var gameFolderPath: DocumentFile? = null
@@ -26,18 +25,8 @@ class HomeViewModel(
     init {
         if (activity != null) {
             sharedPref = PreferenceManager.getDefaultSharedPreferences(activity)
-            activity.storageHelper!!.onFolderSelected = { requestCode, folder ->
-                run {
-                    gameFolderPath = folder
-                    val p = folder.getAbsolutePath(activity!!)
-                    val editor = sharedPref?.edit()
-                    editor?.putString("gameFolder", p)
-                    editor?.apply()
-                    reloadGameList()
-                }
-            }
 
-            val savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
+            savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
 
             if (savedFolder.isNotEmpty()) {
                 try {
@@ -56,16 +45,20 @@ class HomeViewModel(
         }
     }
 
-    fun openGameFolder() {
-        val path = sharedPref?.getString("gameFolder", "") ?: ""
+    fun ensureReloadIfNecessary() {
+        val oldFolder = savedFolder
+        val savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
 
-        if (path.isEmpty())
-            activity?.storageHelper?.storage?.openFolderPicker()
-        else
-            activity?.storageHelper?.storage?.openFolderPicker(
-                activity.storageHelper!!.storage.requestCodeFolderPicker,
-                FileFullPath(activity, path)
+        if(savedFolder.isNotEmpty() && savedFolder != oldFolder) {
+            gameFolderPath = DocumentFileCompat.fromFullPath(
+                mainViewModel?.activity!!,
+                savedFolder,
+                documentType = DocumentFileType.FOLDER,
+                requiresWriteAccess = true
             )
+
+            reloadGameList()
+        }
     }
 
     fun reloadGameList() {
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt
index 5634a8392..d940b2c53 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt
@@ -2,15 +2,28 @@ package org.ryujinx.android.viewmodels
 
 import android.content.SharedPreferences
 import androidx.compose.runtime.MutableState
+import androidx.documentfile.provider.DocumentFile
 import androidx.navigation.NavHostController
 import androidx.preference.PreferenceManager
+import com.anggrayudi.storage.file.FileFullPath
+import com.anggrayudi.storage.file.getAbsolutePath
 import org.ryujinx.android.MainActivity
 
 class SettingsViewModel(var navController: NavHostController, val activity: MainActivity) {
+    private var previousCallback: ((requestCode: Int, folder: DocumentFile) -> Unit)?
     private var sharedPref: SharedPreferences
 
     init {
         sharedPref = getPreferences()
+        previousCallback = activity.storageHelper!!.onFolderSelected
+        activity.storageHelper!!.onFolderSelected = { requestCode, folder ->
+            run {
+                val p = folder.getAbsolutePath(activity!!)
+                val editor = sharedPref?.edit()
+                editor?.putString("gameFolder", p)
+                editor?.apply()
+            }
+        }
     }
 
     private fun getPreferences() : SharedPreferences {
@@ -73,5 +86,20 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
         editor.putBoolean("isGrid", isGrid.value)
 
         editor.apply()
+        activity.storageHelper!!.onFolderSelected = previousCallback
+    }
+
+
+
+    fun openGameFolder() {
+        val path = sharedPref?.getString("gameFolder", "") ?: ""
+
+        if (path.isEmpty())
+            activity?.storageHelper?.storage?.openFolderPicker()
+        else
+            activity?.storageHelper?.storage?.openFolderPicker(
+                activity.storageHelper!!.storage.requestCodeFolderPicker,
+                FileFullPath(activity, path)
+            )
     }
 }
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt
index 27a701861..e8a39b0ce 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt
@@ -33,18 +33,15 @@ import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.Settings
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.AlertDialogDefaults
-import androidx.compose.material3.BottomAppBar
-import androidx.compose.material3.BottomAppBarDefaults
 import androidx.compose.material3.Card
 import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.FloatingActionButton
-import androidx.compose.material3.FloatingActionButtonDefaults
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.LinearProgressIndicator
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.SearchBar
 import androidx.compose.material3.SearchBarDefaults
@@ -55,6 +52,7 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.asImageBitmap
@@ -75,7 +73,7 @@ import kotlin.math.roundToInt
 class HomeViews {
     companion object {
         const val ListImageSize = 150
-        const val GridImageSize = 256
+        const val GridImageSize = 300
 
         @OptIn(ExperimentalMaterial3Api::class)
         @Composable
@@ -88,6 +86,9 @@ class HomeViews {
             val openTitleUpdateDialog = remember { mutableStateOf(false) }
             val canClose = remember { mutableStateOf(true) }
             val openDlcDialog = remember { mutableStateOf(false) }
+            val selectedModel = remember {
+                mutableStateOf(viewModel.mainViewModel?.selected)
+            }
             val query = remember {
                 mutableStateOf("")
             }
@@ -161,138 +162,13 @@ class HomeViews {
                             }
                         }
                     )
-                },
-                bottomBar = {
-                    BottomAppBar(
-                        actions = {
-                            if (showAppActions.value) {
-                                IconButton(onClick = {
-                                    if (viewModel.mainViewModel?.selected != null) {
-                                        thread {
-                                            showLoading.value = true
-                                            val success =
-                                                viewModel.mainViewModel?.loadGame(viewModel.mainViewModel.selected!!)
-                                                    ?: false
-                                            if (success) {
-                                                launchOnUiThread {
-                                                    viewModel.mainViewModel?.navigateToGame()
-                                                }
-                                            } else {
-                                                viewModel.mainViewModel?.selected!!.close()
-                                            }
-                                            showLoading.value = false
-                                        }
-                                    }
-                                }) {
-                                    Icon(
-                                        org.ryujinx.android.Icons.playArrow(MaterialTheme.colorScheme.onSurface),
-                                        contentDescription = "Run"
-                                    )
-                                }
-                                val showAppMenu = remember { mutableStateOf(false) }
-                                Box {
-                                    IconButton(onClick = {
-                                        showAppMenu.value = true
-                                    }) {
-                                        Icon(
-                                            Icons.Filled.Menu,
-                                            contentDescription = "Menu"
-                                        )
-                                    }
-                                    DropdownMenu(
-                                        expanded = showAppMenu.value,
-                                        onDismissRequest = { showAppMenu.value = false }) {
-                                        DropdownMenuItem(text = {
-                                            Text(text = "Clear PPTC Cache")
-                                        }, onClick = {
-                                            showAppMenu.value = false
-                                            viewModel.mainViewModel?.clearPptcCache(
-                                                viewModel.mainViewModel?.selected?.titleId ?: ""
-                                            )
-                                        })
-                                        DropdownMenuItem(text = {
-                                            Text(text = "Purge Shader Cache")
-                                        }, onClick = {
-                                            showAppMenu.value = false
-                                            viewModel.mainViewModel?.purgeShaderCache(
-                                                viewModel.mainViewModel?.selected?.titleId ?: ""
-                                            )
-                                        })
-                                        DropdownMenuItem(text = {
-                                            Text(text = "Manage Updates")
-                                        }, onClick = {
-                                            showAppMenu.value = false
-                                            openTitleUpdateDialog.value = true
-                                        })
-                                        DropdownMenuItem(text = {
-                                            Text(text = "Manage DLC")
-                                        }, onClick = {
-                                            showAppMenu.value = false
-                                            openDlcDialog.value = true
-                                        })
-                                    }
-                                }
-                            }
-
-                            /*\val showAppletMenu = remember { mutableStateOf(false) }
-                            Box {
-                                IconButton(onClick = {
-                                    showAppletMenu.value = true
-                                }) {
-                                    Icon(
-                                        org.ryujinx.android.Icons.applets(MaterialTheme.colorScheme.onSurface),
-                                        contentDescription = "Applets"
-                                    )
-                                }
-                                DropdownMenu(
-                                    expanded = showAppletMenu.value,
-                                    onDismissRequest = { showAppletMenu.value = false }) {
-                                    DropdownMenuItem(text = {
-                                        Text(text = "Launch Mii Editor")
-                                    }, onClick = {
-                                        showAppletMenu.value = false
-                                        showLoading.value = true
-                                        thread {
-                                            val success =
-                                                viewModel.mainViewModel?.loadMiiEditor() ?: false
-                                            if (success) {
-                                                launchOnUiThread {
-                                                    viewModel.mainViewModel?.navigateToGame()
-                                                }
-                                            } else
-                                                viewModel.mainViewModel!!.isMiiEditorLaunched = false
-                                            showLoading.value = false
-                                        }
-                                    })
-                                }
-                            }*/
-                        },
-                        floatingActionButton = {
-                            FloatingActionButton(
-                                onClick = {
-                                    viewModel.openGameFolder()
-                                },
-                                containerColor = BottomAppBarDefaults.bottomAppBarFabColor,
-                                elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation()
-                            ) {
-                                Icon(
-                                    org.ryujinx.android.Icons.folderOpen(MaterialTheme.colorScheme.onSurface),
-                                    contentDescription = "Open Folder"
-                                )
-                            }
-                        }
-                    )
                 }
 
-
             ) { contentPadding ->
                 Box(modifier = Modifier.padding(contentPadding)) {
                     val list = remember {
                         viewModel.gameList
                     }
-                    val selectedModel = remember {
-                        mutableStateOf(viewModel.mainViewModel?.selected)
-                    }
                     var settings = QuickSettings(viewModel.activity!!)
 
                     if (settings.isGrid) {
@@ -301,13 +177,13 @@ class HomeViews {
                             columns = GridCells.Adaptive(minSize = (size + 4).dp),
                             modifier = Modifier
                                 .fillMaxSize()
+                                .padding(4.dp),
+                            horizontalArrangement = Arrangement.SpaceEvenly
                         ) {
                             items(list) {
                                 it.titleName?.apply {
                                     if (this.isNotEmpty() && (query.value.trim()
-                                            .isEmpty() || this.lowercase(
-                                            Locale.getDefault()
-                                        )
+                                            .isEmpty() || this.lowercase(Locale.getDefault())
                                             .contains(query.value))
                                     )
                                         GridGameItem(
@@ -335,7 +211,7 @@ class HomeViews {
                                             viewModel,
                                             showAppActions,
                                             showLoading,
-                                            selectedModel
+                                            selectedModel,
                                         )
                                 }
                             }
@@ -405,6 +281,89 @@ class HomeViews {
                     }
                 }
             }
+
+            if (showAppActions.value)
+                ModalBottomSheet(
+                    content = {
+                        Row(
+                            modifier = Modifier.padding(8.dp),
+                            horizontalArrangement = Arrangement.SpaceEvenly
+                        ) {
+                            if (showAppActions.value) {
+                                IconButton(onClick = {
+                                    if (viewModel.mainViewModel?.selected != null) {
+                                        thread {
+                                            showLoading.value = true
+                                            val success =
+                                                viewModel.mainViewModel?.loadGame(viewModel.mainViewModel.selected!!)
+                                                    ?: false
+                                            if (success) {
+                                                launchOnUiThread {
+                                                    viewModel.mainViewModel?.navigateToGame()
+                                                }
+                                            } else {
+                                                viewModel.mainViewModel?.selected!!.close()
+                                            }
+                                            showLoading.value = false
+                                        }
+                                    }
+                                }) {
+                                    Icon(
+                                        org.ryujinx.android.Icons.playArrow(MaterialTheme.colorScheme.onSurface),
+                                        contentDescription = "Run"
+                                    )
+                                }
+                                val showAppMenu = remember { mutableStateOf(false) }
+                                Box {
+                                    IconButton(onClick = {
+                                        showAppMenu.value = true
+                                    }) {
+                                        Icon(
+                                            Icons.Filled.Menu,
+                                            contentDescription = "Menu"
+                                        )
+                                    }
+                                    DropdownMenu(
+                                        expanded = showAppMenu.value,
+                                        onDismissRequest = { showAppMenu.value = false }) {
+                                        DropdownMenuItem(text = {
+                                            Text(text = "Clear PPTC Cache")
+                                        }, onClick = {
+                                            showAppMenu.value = false
+                                            viewModel.mainViewModel?.clearPptcCache(
+                                                viewModel.mainViewModel?.selected?.titleId ?: ""
+                                            )
+                                        })
+                                        DropdownMenuItem(text = {
+                                            Text(text = "Purge Shader Cache")
+                                        }, onClick = {
+                                            showAppMenu.value = false
+                                            viewModel.mainViewModel?.purgeShaderCache(
+                                                viewModel.mainViewModel?.selected?.titleId ?: ""
+                                            )
+                                        })
+                                        DropdownMenuItem(text = {
+                                            Text(text = "Manage Updates")
+                                        }, onClick = {
+                                            showAppMenu.value = false
+                                            openTitleUpdateDialog.value = true
+                                        })
+                                        DropdownMenuItem(text = {
+                                            Text(text = "Manage DLC")
+                                        }, onClick = {
+                                            showAppMenu.value = false
+                                            openDlcDialog.value = true
+                                        })
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    onDismissRequest = {
+                        showAppActions.value = false
+                        selectedModel.value = null
+                    }
+                )
         }
 
         @OptIn(ExperimentalFoundationApi::class)
@@ -548,7 +507,7 @@ class HomeViews {
                             selectedModel.value = gameModel
                         })
             ) {
-                Column {
+                Column(modifier = Modifier.padding(4.dp)) {
                     if (!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000") {
                         if (gameModel.icon?.isNotEmpty() == true) {
                             val pic = decoder.decode(gameModel.icon)
@@ -560,6 +519,7 @@ class HomeViews {
                                 modifier = Modifier
                                     .padding(0.dp)
                                     .clip(RoundedCornerShape(16.dp))
+                                    .align(Alignment.CenterHorizontally)
                             )
                         } else NotAvailableIcon()
                     } else NotAvailableIcon()
@@ -567,13 +527,15 @@ class HomeViews {
                         text = gameModel.titleName ?: "N/A",
                         maxLines = 1,
                         overflow = TextOverflow.Ellipsis,
-                        modifier = Modifier.basicMarquee()
+                        modifier = Modifier.padding(vertical = 4.dp)
+                            .basicMarquee()
                     )
                     Text(
                         text = gameModel.developer ?: "N/A",
                         maxLines = 1,
                         overflow = TextOverflow.Ellipsis,
-                        modifier = Modifier.basicMarquee()
+                        modifier = Modifier.padding(vertical = 4.dp)
+                            .basicMarquee()
                     )
                 }
             }
@@ -584,7 +546,7 @@ class HomeViews {
             val size = ListImageSize / Resources.getSystem().displayMetrics.density
             Icon(
                 Icons.Filled.Add,
-                contentDescription = "Options",
+                contentDescription = "N/A",
                 modifier = Modifier
                     .padding(end = 8.dp)
                     .width(size.roundToInt().dp)
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt
index ead155a51..629d75886 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt
@@ -167,6 +167,23 @@ class SettingViews {
                                     isGrid.value = !isGrid.value
                                 })
                             }
+                            Row(
+                                modifier = Modifier
+                                    .fillMaxWidth()
+                                    .padding(8.dp),
+                                horizontalArrangement = Arrangement.SpaceBetween,
+                                verticalAlignment = Alignment.CenterVertically
+                            ) {
+                                Text(
+                                    text = "Game Folder",
+                                    modifier = Modifier.align(Alignment.CenterVertically)
+                                )
+                                Button(onClick = {
+                                    settingsViewModel.openGameFolder()
+                                }) {
+                                    Text(text = "Choose Folder")
+                                }
+                            }
                         }
                     }
                     ExpandableView(onCardArrowClick = { }, title = "System") {