android - adjust grid view design, remove bottom app bar

This commit is contained in:
Emmanuel Hansen 2023-11-12 19:36:10 +00:00
parent bc6e5de507
commit 9765f7a388
5 changed files with 161 additions and 161 deletions

View File

@ -11,8 +11,8 @@ android {
applicationId "org.ryujinx.android" applicationId "org.ryujinx.android"
minSdk 30 minSdk 30
targetSdk 33 targetSdk 33
versionCode 10004 versionCode 10006
versionName '1.0.4' versionName '1.0.6'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View File

@ -6,9 +6,7 @@ import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.anggrayudi.storage.file.DocumentFileCompat import com.anggrayudi.storage.file.DocumentFileCompat
import com.anggrayudi.storage.file.DocumentFileType import com.anggrayudi.storage.file.DocumentFileType
import com.anggrayudi.storage.file.FileFullPath
import com.anggrayudi.storage.file.extension import com.anggrayudi.storage.file.extension
import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.search import com.anggrayudi.storage.file.search
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -17,6 +15,7 @@ class HomeViewModel(
val activity: MainActivity? = null, val activity: MainActivity? = null,
val mainViewModel: MainViewModel? = null val mainViewModel: MainViewModel? = null
) { ) {
private var savedFolder: String = ""
private var isLoading: Boolean = false private var isLoading: Boolean = false
private var loadedCache: List<GameModel> = listOf() private var loadedCache: List<GameModel> = listOf()
private var gameFolderPath: DocumentFile? = null private var gameFolderPath: DocumentFile? = null
@ -26,18 +25,8 @@ class HomeViewModel(
init { init {
if (activity != null) { if (activity != null) {
sharedPref = PreferenceManager.getDefaultSharedPreferences(activity) 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()) { if (savedFolder.isNotEmpty()) {
try { try {
@ -56,16 +45,20 @@ class HomeViewModel(
} }
} }
fun openGameFolder() { fun ensureReloadIfNecessary() {
val path = sharedPref?.getString("gameFolder", "") ?: "" val oldFolder = savedFolder
val savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
if (path.isEmpty()) if(savedFolder.isNotEmpty() && savedFolder != oldFolder) {
activity?.storageHelper?.storage?.openFolderPicker() gameFolderPath = DocumentFileCompat.fromFullPath(
else mainViewModel?.activity!!,
activity?.storageHelper?.storage?.openFolderPicker( savedFolder,
activity.storageHelper!!.storage.requestCodeFolderPicker, documentType = DocumentFileType.FOLDER,
FileFullPath(activity, path) requiresWriteAccess = true
) )
reloadGameList()
}
} }
fun reloadGameList() { fun reloadGameList() {

View File

@ -2,15 +2,28 @@ package org.ryujinx.android.viewmodels
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.documentfile.provider.DocumentFile
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.anggrayudi.storage.file.FileFullPath
import com.anggrayudi.storage.file.getAbsolutePath
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
class SettingsViewModel(var navController: NavHostController, val activity: MainActivity) { class SettingsViewModel(var navController: NavHostController, val activity: MainActivity) {
private var previousCallback: ((requestCode: Int, folder: DocumentFile) -> Unit)?
private var sharedPref: SharedPreferences private var sharedPref: SharedPreferences
init { init {
sharedPref = getPreferences() 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 { private fun getPreferences() : SharedPreferences {
@ -73,5 +86,20 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
editor.putBoolean("isGrid", isGrid.value) editor.putBoolean("isGrid", isGrid.value)
editor.apply() 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)
)
} }
} }

View File

@ -33,18 +33,15 @@ import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.BottomAppBarDefaults
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.SearchBarDefaults
@ -55,6 +52,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
@ -75,7 +73,7 @@ import kotlin.math.roundToInt
class HomeViews { class HomeViews {
companion object { companion object {
const val ListImageSize = 150 const val ListImageSize = 150
const val GridImageSize = 256 const val GridImageSize = 300
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -88,6 +86,9 @@ class HomeViews {
val openTitleUpdateDialog = remember { mutableStateOf(false) } val openTitleUpdateDialog = remember { mutableStateOf(false) }
val canClose = remember { mutableStateOf(true) } val canClose = remember { mutableStateOf(true) }
val openDlcDialog = remember { mutableStateOf(false) } val openDlcDialog = remember { mutableStateOf(false) }
val selectedModel = remember {
mutableStateOf(viewModel.mainViewModel?.selected)
}
val query = remember { val query = remember {
mutableStateOf("") 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 -> ) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding)) { Box(modifier = Modifier.padding(contentPadding)) {
val list = remember { val list = remember {
viewModel.gameList viewModel.gameList
} }
val selectedModel = remember {
mutableStateOf(viewModel.mainViewModel?.selected)
}
var settings = QuickSettings(viewModel.activity!!) var settings = QuickSettings(viewModel.activity!!)
if (settings.isGrid) { if (settings.isGrid) {
@ -301,13 +177,13 @@ class HomeViews {
columns = GridCells.Adaptive(minSize = (size + 4).dp), columns = GridCells.Adaptive(minSize = (size + 4).dp),
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(4.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) { ) {
items(list) { items(list) {
it.titleName?.apply { it.titleName?.apply {
if (this.isNotEmpty() && (query.value.trim() if (this.isNotEmpty() && (query.value.trim()
.isEmpty() || this.lowercase( .isEmpty() || this.lowercase(Locale.getDefault())
Locale.getDefault()
)
.contains(query.value)) .contains(query.value))
) )
GridGameItem( GridGameItem(
@ -335,7 +211,7 @@ class HomeViews {
viewModel, viewModel,
showAppActions, showAppActions,
showLoading, 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) @OptIn(ExperimentalFoundationApi::class)
@ -548,7 +507,7 @@ class HomeViews {
selectedModel.value = gameModel selectedModel.value = gameModel
}) })
) { ) {
Column { Column(modifier = Modifier.padding(4.dp)) {
if (!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000") { if (!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000") {
if (gameModel.icon?.isNotEmpty() == true) { if (gameModel.icon?.isNotEmpty() == true) {
val pic = decoder.decode(gameModel.icon) val pic = decoder.decode(gameModel.icon)
@ -560,6 +519,7 @@ class HomeViews {
modifier = Modifier modifier = Modifier
.padding(0.dp) .padding(0.dp)
.clip(RoundedCornerShape(16.dp)) .clip(RoundedCornerShape(16.dp))
.align(Alignment.CenterHorizontally)
) )
} else NotAvailableIcon() } else NotAvailableIcon()
} else NotAvailableIcon() } else NotAvailableIcon()
@ -567,13 +527,15 @@ class HomeViews {
text = gameModel.titleName ?: "N/A", text = gameModel.titleName ?: "N/A",
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.basicMarquee() modifier = Modifier.padding(vertical = 4.dp)
.basicMarquee()
) )
Text( Text(
text = gameModel.developer ?: "N/A", text = gameModel.developer ?: "N/A",
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, 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 val size = ListImageSize / Resources.getSystem().displayMetrics.density
Icon( Icon(
Icons.Filled.Add, Icons.Filled.Add,
contentDescription = "Options", contentDescription = "N/A",
modifier = Modifier modifier = Modifier
.padding(end = 8.dp) .padding(end = 8.dp)
.width(size.roundToInt().dp) .width(size.roundToInt().dp)

View File

@ -167,6 +167,23 @@ class SettingViews {
isGrid.value = !isGrid.value 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") { ExpandableView(onCardArrowClick = { }, title = "System") {