merged 2.0.3 Layout into 2.0.4

This commit is contained in:
BeZide93
2025-09-25 17:10:02 -05:00
committed by KeatonTheBot
parent 48ba1cc9ed
commit be176fd367
5 changed files with 129 additions and 146 deletions
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature <uses-feature
android:name="android.hardware.audio.output" android:name="android.hardware.audio.output"
@@ -49,6 +50,12 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<!--
<intent-filter>
<action android:name="org.kenji.android.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
-->
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="org.kenjinx.android.fileprovider" android:authorities="org.kenjinx.android.fileprovider"
@@ -60,7 +67,7 @@
</provider> </provider>
<provider <provider
android:name=".providers.DocumentProvider" android:name=".providers.DocumentProvider"
android:authorities="org.kenjinx.android.providers" android:authorities="${applicationId}.providers"
android:exported="true" android:exported="true"
android:grantUriPermissions="true" android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS"> android:permission="android.permission.MANAGE_DOCUMENTS">
@@ -107,11 +107,6 @@ class HomeViewModel(
for(game in loadedCache) for(game in loadedCache)
{ {
game.getGameInfo() game.getGameInfo()
if(game.isUnknown())
{
loadedCache.remove(game);
}
} }
} finally { } finally {
isLoading.value = false isLoading.value = false
@@ -49,12 +49,12 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment 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
@@ -66,6 +66,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@@ -109,18 +110,16 @@ class HomeViews {
val selectedModel = remember { mutableStateOf(viewModel.mainViewModel?.selected) } val selectedModel = remember { mutableStateOf(viewModel.mainViewModel?.selected) }
val query = remember { mutableStateOf("") } val query = remember { mutableStateOf("") }
var refreshUser by remember { mutableStateOf(true) } var refreshUser by remember { mutableStateOf(true) }
var isFabVisible by remember { mutableStateOf(true)} var isFabVisible by remember { mutableStateOf(true) }
val isNavigating = remember { mutableStateOf(false) } val isNavigating = remember { mutableStateOf(false) }
val context = LocalContext.current
val nestedScrollConnection = remember { val nestedScrollConnection = remember {
object : NestedScrollConnection { object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (available.y < -1) { if (available.y < -1) isFabVisible = false
isFabVisible = false if (available.y > 1) isFabVisible = true
}
if (available.y > 1) {
isFabVisible = true
}
return Offset.Zero return Offset.Zero
} }
} }
@@ -132,7 +131,7 @@ class HomeViews {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp,vertical = 8.dp) .padding(horizontal = 16.dp, vertical = 8.dp)
.background(MaterialTheme.colorScheme.surface), .background(MaterialTheme.colorScheme.surface),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -162,7 +161,6 @@ class HomeViews {
.clickable { .clickable {
if (!isNavigating.value) { if (!isNavigating.value) {
isNavigating.value = true isNavigating.value = true
val currentRoute = navController?.currentDestination?.route val currentRoute = navController?.currentDestination?.route
if (currentRoute != "user") { if (currentRoute != "user") {
navController?.navigate("user") { navController?.navigate("user") {
@@ -170,7 +168,6 @@ class HomeViews {
restoreState = true restoreState = true
} }
} }
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
delay(500) delay(500)
isNavigating.value = false isNavigating.value = false
@@ -181,7 +178,6 @@ class HomeViews {
) { ) {
if (refreshUser && viewModel.mainViewModel?.userViewModel?.openedUser?.userPicture?.isNotEmpty() == true) { if (refreshUser && viewModel.mainViewModel?.userViewModel?.openedUser?.userPicture?.isNotEmpty() == true) {
val pic = viewModel.mainViewModel.userViewModel.openedUser.userPicture val pic = viewModel.mainViewModel.userViewModel.openedUser.userPicture
Image( Image(
bitmap = BitmapFactory.decodeByteArray( bitmap = BitmapFactory.decodeByteArray(
pic, pic,
@@ -201,11 +197,12 @@ class HomeViews {
) )
} }
} }
// Settings
IconButton( IconButton(
onClick = { onClick = {
if (!isNavigating.value) { if (!isNavigating.value) {
isNavigating.value = true isNavigating.value = true
val currentRoute = navController?.currentDestination?.route val currentRoute = navController?.currentDestination?.route
if (currentRoute != "settings") { if (currentRoute != "settings") {
navController?.navigate("settings") { navController?.navigate("settings") {
@@ -213,7 +210,6 @@ class HomeViews {
restoreState = true restoreState = true
} }
} }
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
delay(500) delay(500)
isNavigating.value = false isNavigating.value = false
@@ -229,33 +225,21 @@ class HomeViews {
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) )
) { ) {
Icon( Icon(Icons.Filled.Settings, contentDescription = "Settings")
Icons.Filled.Settings,
contentDescription = "Settings"
)
} }
} }
OutlinedTextField( OutlinedTextField(
value = query.value, value = query.value,
onValueChange = { onValueChange = { query.value = it },
query.value = it
},
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(56.dp), .height(56.dp),
placeholder = { placeholder = {
Text( Text("Search...", modifier = Modifier.padding(bottom = 4.dp))
"Search...",
modifier = Modifier.padding(bottom = 4.dp)
)
},
leadingIcon = {
Icon(
Icons.Filled.Search,
contentDescription = "Search"
)
}, },
leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search") },
singleLine = true, singleLine = true,
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
colors = TextFieldDefaults.outlinedTextFieldColors( colors = TextFieldDefaults.outlinedTextFieldColors(
@@ -289,11 +273,9 @@ class HomeViews {
viewModel.filter(query.value) viewModel.filter(query.value)
if (!isPreview) { if (!isPreview) {
var settings = QuickSettings(viewModel.activity!!) val settings = QuickSettings(viewModel.activity!!)
if (isLoading.value) { if (isLoading.value) {
Box(modifier = Modifier.fillMaxSize()) Box(modifier = Modifier.fillMaxSize()) {
{
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier modifier = Modifier
.width(64.dp) .width(64.dp)
@@ -304,8 +286,7 @@ class HomeViews {
} }
} else { } else {
if (settings.isGrid) { if (settings.isGrid) {
val size = val size = GridImageSize / Resources.getSystem().displayMetrics.density
GridImageSize / Resources.getSystem().displayMetrics.density
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = (size + 4).dp), columns = GridCells.Adaptive(minSize = (size + 4).dp),
modifier = Modifier modifier = Modifier
@@ -318,8 +299,7 @@ class HomeViews {
it.titleName?.apply { it.titleName?.apply {
if (this.isNotEmpty() && (query.value.trim() if (this.isNotEmpty() && (query.value.trim()
.isEmpty() || this.lowercase(Locale.getDefault()) .isEmpty() || this.lowercase(Locale.getDefault())
.contains(query.value)) .contains(query.value))) {
)
GridGameItem( GridGameItem(
it, it,
viewModel, viewModel,
@@ -328,6 +308,7 @@ class HomeViews {
selectedModel, selectedModel,
showError showError
) )
}
} }
} }
} }
@@ -336,11 +317,8 @@ class HomeViews {
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))
)
Box(modifier = Modifier.animateItemPlacement()) { Box(modifier = Modifier.animateItemPlacement()) {
ListGameItem( ListGameItem(
it, it,
@@ -351,6 +329,7 @@ class HomeViews {
showError showError
) )
} }
}
} }
} }
} }
@@ -381,27 +360,27 @@ class HomeViews {
} }
} }
if(viewModel.mainViewModel?.loadGameModel?.value != null) if (viewModel.mainViewModel?.loadGameModel?.value != null)
LaunchedEffect(viewModel.mainViewModel.loadGameModel.value) { LaunchedEffect(viewModel.mainViewModel.loadGameModel.value) {
if (viewModel.mainViewModel.bootPath.value == "gameItem_${viewModel.mainViewModel.loadGameModel.value!!.titleName}") { if (viewModel.mainViewModel.bootPath.value ==
"gameItem_${viewModel.mainViewModel.loadGameModel.value!!.titleName}"
) {
viewModel.mainViewModel.bootPath.value = null viewModel.mainViewModel.bootPath.value = null
thread { thread {
showLoading.value = true showLoading.value = true
val success = val success = viewModel.mainViewModel.loadGame(
viewModel.mainViewModel.loadGame( viewModel.mainViewModel.loadGameModel.value!!,
viewModel.mainViewModel.loadGameModel.value!!, true,
true, viewModel.mainViewModel.forceNceAndPptc.value
viewModel.mainViewModel.forceNceAndPptc.value ) ?: false
) ?: false
if (success == 1) { if (success == 1) {
launchOnUiThread { launchOnUiThread {
viewModel.mainViewModel.navigateToGame() viewModel.mainViewModel.navigateToGame()
} }
} else { } else {
if (success == -2) if (success == -2)
showError.value = showError.value = "Error loading update. Please re-add update file"
"Error loading update. Please re-add update file"
viewModel.mainViewModel.loadGameModel.value!!.close() viewModel.mainViewModel.loadGameModel.value!!.close()
} }
showLoading.value = false showLoading.value = false
@@ -421,8 +400,9 @@ class HomeViews {
if (viewModel.mainViewModel?.selected != null) { if (viewModel.mainViewModel?.selected != null) {
thread { thread {
showLoading.value = true showLoading.value = true
val success = val success = viewModel.mainViewModel.loadGame(
viewModel.mainViewModel.loadGame(viewModel.mainViewModel.selected!!) viewModel.mainViewModel.selected!!
)
if (success == 1) { if (success == 1) {
launchOnUiThread { launchOnUiThread {
viewModel.mainViewModel.navigateToGame() viewModel.mainViewModel.navigateToGame()
@@ -444,53 +424,54 @@ class HomeViews {
} }
val showAppMenu = remember { mutableStateOf(false) } val showAppMenu = remember { mutableStateOf(false) }
Box { Box {
IconButton(onClick = { IconButton(onClick = { showAppMenu.value = true }) {
showAppMenu.value = true Icon(Icons.Filled.Menu, contentDescription = "Menu")
}) {
Icon(
Icons.Filled.Menu,
contentDescription = "Menu"
)
} }
DropdownMenu( DropdownMenu(
expanded = showAppMenu.value, expanded = showAppMenu.value,
onDismissRequest = { showAppMenu.value = false }) { onDismissRequest = { showAppMenu.value = false }
DropdownMenuItem(text = { ) {
Text(text = "Clear PPTC Cache") DropdownMenuItem(
}, onClick = { text = { Text(text = "Clear PPTC Cache") },
showAppMenu.value = false onClick = {
viewModel.mainViewModel?.clearPptcCache( showAppMenu.value = false
viewModel.mainViewModel.selected?.titleId ?: "" viewModel.mainViewModel?.clearPptcCache(
) viewModel.mainViewModel.selected?.titleId ?: ""
}) )
DropdownMenuItem(text = { }
Text(text = "Purge Shader Cache") )
}, onClick = { DropdownMenuItem(
showAppMenu.value = false text = { Text(text = "Purge Shader Cache") },
viewModel.mainViewModel?.purgeShaderCache( onClick = {
viewModel.mainViewModel.selected?.titleId ?: "" showAppMenu.value = false
) viewModel.mainViewModel?.purgeShaderCache(
}) viewModel.mainViewModel.selected?.titleId ?: ""
DropdownMenuItem(text = { )
Text(text = "Delete All Cache") }
}, onClick = { )
showAppMenu.value = false DropdownMenuItem(
viewModel.mainViewModel?.deleteCache( text = { Text(text = "Delete All Cache") },
viewModel.mainViewModel.selected?.titleId ?: "" onClick = {
) showAppMenu.value = false
}) viewModel.mainViewModel?.deleteCache(
DropdownMenuItem(text = { viewModel.mainViewModel.selected?.titleId ?: ""
Text(text = "Manage Updates") )
}, onClick = { }
showAppMenu.value = false )
openTitleUpdateDialog.value = true DropdownMenuItem(
}) text = { Text(text = "Manage Updates") },
DropdownMenuItem(text = { onClick = {
Text(text = "Manage DLC") showAppMenu.value = false
}, onClick = { openTitleUpdateDialog.value = true
showAppMenu.value = false }
openDlcDialog.value = true )
}) DropdownMenuItem(
text = { Text(text = "Manage DLC") },
onClick = {
showAppMenu.value = false
openDlcDialog.value = true
}
)
} }
} }
} }
@@ -513,9 +494,7 @@ class HomeViews {
selectedModel: MutableState<GameModel?>, selectedModel: MutableState<GameModel?>,
showError: MutableState<String> showError: MutableState<String>
) { ) {
remember { remember { selectedModel }
selectedModel
}
val color = val color =
if (selectedModel.value == gameModel) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface if (selectedModel.value == gameModel) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface
@@ -530,19 +509,17 @@ class HomeViews {
onClick = { onClick = {
if (viewModel.mainViewModel?.selected != null) { if (viewModel.mainViewModel?.selected != null) {
showAppActions.value = false showAppActions.value = false
viewModel.mainViewModel.apply { viewModel.mainViewModel.apply { selected = null }
selected = null
}
selectedModel.value = null selectedModel.value = null
} else if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000" || gameModel.type == FileType.Nro) { } else if (gameModel.titleId.isNullOrEmpty()
|| gameModel.titleId != "0000000000000000"
|| gameModel.type == FileType.Nro
) {
thread { thread {
showLoading.value = true showLoading.value = true
val success = val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false
viewModel.mainViewModel?.loadGame(gameModel) ?: false
if (success == 1) { if (success == 1) {
launchOnUiThread { launchOnUiThread { viewModel.mainViewModel?.navigateToGame() }
viewModel.mainViewModel?.navigateToGame()
}
} else { } else {
if (success == -2) if (success == -2)
showError.value = showError.value =
@@ -557,7 +534,8 @@ class HomeViews {
viewModel.mainViewModel?.selected = gameModel viewModel.mainViewModel?.selected = gameModel
showAppActions.value = true showAppActions.value = true
selectedModel.value = gameModel selectedModel.value = gameModel
}) }
)
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -566,22 +544,21 @@ class HomeViews {
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Row { Row {
if (!gameModel.titleId.isNullOrEmpty() && (gameModel.titleId != "0000000000000000" || gameModel.type == FileType.Nro)) { if (!gameModel.titleId.isNullOrEmpty()
&& (gameModel.titleId != "0000000000000000" || gameModel.type == FileType.Nro)
) {
if (gameModel.icon?.isNotEmpty() == true) { if (gameModel.icon?.isNotEmpty() == true) {
val pic = decoder.decode(gameModel.icon) val pic = decoder.decode(gameModel.icon)
val size = val size = ListImageSize / Resources.getSystem().displayMetrics.density
ListImageSize / Resources.getSystem().displayMetrics.density
Image( Image(
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size) bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size).asImageBitmap(),
.asImageBitmap(),
contentDescription = gameModel.titleName + " icon", contentDescription = gameModel.titleName + " icon",
modifier = Modifier modifier = Modifier
.padding(end = 8.dp) .padding(end = 8.dp)
.width(size.roundToInt().dp) .width(size.roundToInt().dp)
.height(size.roundToInt().dp) .height(size.roundToInt().dp)
) )
} else if (gameModel.type == FileType.Nro) } else if (gameModel.type == FileType.Nro) NROIcon()
NROIcon()
else NotAvailableIcon() else NotAvailableIcon()
} else NotAvailableIcon() } else NotAvailableIcon()
Column { Column {
@@ -608,9 +585,7 @@ class HomeViews {
selectedModel: MutableState<GameModel?>, selectedModel: MutableState<GameModel?>,
showError: MutableState<String> showError: MutableState<String>
) { ) {
remember { remember { selectedModel }
selectedModel
}
val color = val color =
if (selectedModel.value == gameModel) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface if (selectedModel.value == gameModel) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface
@@ -625,19 +600,17 @@ class HomeViews {
onClick = { onClick = {
if (viewModel.mainViewModel?.selected != null) { if (viewModel.mainViewModel?.selected != null) {
showAppActions.value = false showAppActions.value = false
viewModel.mainViewModel.apply { viewModel.mainViewModel.apply { selected = null }
selected = null
}
selectedModel.value = null selectedModel.value = null
} else if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000" || gameModel.type == FileType.Nro) { } else if (gameModel.titleId.isNullOrEmpty()
|| gameModel.titleId != "0000000000000000"
|| gameModel.type == FileType.Nro
) {
thread { thread {
showLoading.value = true showLoading.value = true
val success = val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false
viewModel.mainViewModel?.loadGame(gameModel) ?: false
if (success == 1) { if (success == 1) {
launchOnUiThread { launchOnUiThread { viewModel.mainViewModel?.navigateToGame() }
viewModel.mainViewModel?.navigateToGame()
}
} else { } else {
if (success == -2) if (success == -2)
showError.value = showError.value =
@@ -652,23 +625,24 @@ class HomeViews {
viewModel.mainViewModel?.selected = gameModel viewModel.mainViewModel?.selected = gameModel
showAppActions.value = true showAppActions.value = true
selectedModel.value = gameModel selectedModel.value = gameModel
}) }
)
) { ) {
Column(modifier = Modifier.padding(4.dp)) { Column(modifier = Modifier.padding(4.dp)) {
if (!gameModel.titleId.isNullOrEmpty() && (gameModel.titleId != "0000000000000000" || gameModel.type == FileType.Nro)) { if (!gameModel.titleId.isNullOrEmpty()
&& (gameModel.titleId != "0000000000000000" || gameModel.type == FileType.Nro)
) {
if (gameModel.icon?.isNotEmpty() == true) { if (gameModel.icon?.isNotEmpty() == true) {
val pic = decoder.decode(gameModel.icon) val pic = decoder.decode(gameModel.icon)
Image( Image(
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size) bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size).asImageBitmap(),
.asImageBitmap(),
contentDescription = gameModel.titleName + " icon", contentDescription = gameModel.titleName + " icon",
modifier = Modifier modifier = Modifier
.padding(0.dp) .padding(0.dp)
.clip(RoundedCornerShape(16.dp)) .clip(RoundedCornerShape(16.dp))
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
) )
} else if (gameModel.type == FileType.Nro) } else if (gameModel.type == FileType.Nro) NROIcon()
NROIcon()
else NotAvailableIcon() else NotAvailableIcon()
} else NotAvailableIcon() } else NotAvailableIcon()
Text( Text(
@@ -708,7 +682,6 @@ class HomeViews {
.height(size.roundToInt().dp) .height(size.roundToInt().dp)
) )
} }
} }
@Preview @Preview
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Dummy definition that contains no Android items.
The real Android flags come in values-v21/styles.xml -->
<style name="Theme.KenjinxAndroid.Transparent" parent="Theme.Material3.DayNight.NoActionBar"/>
</resources>
@@ -2,4 +2,5 @@
<resources> <resources>
<style name="Theme.KenjinxAndroid" parent="android:Theme.Material.Light.NoActionBar" /> <style name="Theme.KenjinxAndroid" parent="android:Theme.Material.Light.NoActionBar" />
</resources> </resources>