merged 2.0.3 Layout into 2.0.4

This commit is contained in:
BeZide93 2025-09-05 07:52:47 -05:00 committed by KeatonTheBot
parent 48ba1cc9ed
commit be176fd367
5 changed files with 129 additions and 146 deletions

View file

@ -1,5 +1,6 @@
<?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
android:name="android.hardware.audio.output"
@ -49,6 +50,12 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!--
<intent-filter>
<action android:name="org.kenji.android.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.kenjinx.android.fileprovider"
@ -60,7 +67,7 @@
</provider>
<provider
android:name=".providers.DocumentProvider"
android:authorities="org.kenjinx.android.providers"
android:authorities="${applicationId}.providers"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS">

View file

@ -107,11 +107,6 @@ class HomeViewModel(
for(game in loadedCache)
{
game.getGameInfo()
if(game.isUnknown())
{
loadedCache.remove(game);
}
}
} finally {
isLoading.value = false

View file

@ -49,12 +49,12 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@ -109,18 +110,16 @@ class HomeViews {
val selectedModel = remember { mutableStateOf(viewModel.mainViewModel?.selected) }
val query = remember { mutableStateOf("") }
var refreshUser by remember { mutableStateOf(true) }
var isFabVisible by remember { mutableStateOf(true)}
var isFabVisible by remember { mutableStateOf(true) }
val isNavigating = remember { mutableStateOf(false) }
val context = LocalContext.current
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (available.y < -1) {
isFabVisible = false
}
if (available.y > 1) {
isFabVisible = true
}
if (available.y < -1) isFabVisible = false
if (available.y > 1) isFabVisible = true
return Offset.Zero
}
}
@ -132,7 +131,7 @@ class HomeViews {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp,vertical = 8.dp)
.padding(horizontal = 16.dp, vertical = 8.dp)
.background(MaterialTheme.colorScheme.surface),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
@ -162,7 +161,6 @@ class HomeViews {
.clickable {
if (!isNavigating.value) {
isNavigating.value = true
val currentRoute = navController?.currentDestination?.route
if (currentRoute != "user") {
navController?.navigate("user") {
@ -170,7 +168,6 @@ class HomeViews {
restoreState = true
}
}
CoroutineScope(Dispatchers.Main).launch {
delay(500)
isNavigating.value = false
@ -181,7 +178,6 @@ class HomeViews {
) {
if (refreshUser && viewModel.mainViewModel?.userViewModel?.openedUser?.userPicture?.isNotEmpty() == true) {
val pic = viewModel.mainViewModel.userViewModel.openedUser.userPicture
Image(
bitmap = BitmapFactory.decodeByteArray(
pic,
@ -201,11 +197,12 @@ class HomeViews {
)
}
}
// Settings
IconButton(
onClick = {
if (!isNavigating.value) {
isNavigating.value = true
val currentRoute = navController?.currentDestination?.route
if (currentRoute != "settings") {
navController?.navigate("settings") {
@ -213,7 +210,6 @@ class HomeViews {
restoreState = true
}
}
CoroutineScope(Dispatchers.Main).launch {
delay(500)
isNavigating.value = false
@ -229,33 +225,21 @@ class HomeViews {
shape = RoundedCornerShape(8.dp)
)
) {
Icon(
Icons.Filled.Settings,
contentDescription = "Settings"
)
Icon(Icons.Filled.Settings, contentDescription = "Settings")
}
}
OutlinedTextField(
value = query.value,
onValueChange = {
query.value = it
},
onValueChange = { query.value = it },
modifier = Modifier
.weight(1f)
.height(56.dp),
placeholder = {
Text(
"Search...",
modifier = Modifier.padding(bottom = 4.dp)
)
},
leadingIcon = {
Icon(
Icons.Filled.Search,
contentDescription = "Search"
)
Text("Search...", modifier = Modifier.padding(bottom = 4.dp))
},
leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search") },
singleLine = true,
shape = RoundedCornerShape(8.dp),
colors = TextFieldDefaults.outlinedTextFieldColors(
@ -289,11 +273,9 @@ class HomeViews {
viewModel.filter(query.value)
if (!isPreview) {
var settings = QuickSettings(viewModel.activity!!)
val settings = QuickSettings(viewModel.activity!!)
if (isLoading.value) {
Box(modifier = Modifier.fillMaxSize())
{
Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator(
modifier = Modifier
.width(64.dp)
@ -304,8 +286,7 @@ class HomeViews {
}
} else {
if (settings.isGrid) {
val size =
GridImageSize / Resources.getSystem().displayMetrics.density
val size = GridImageSize / Resources.getSystem().displayMetrics.density
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = (size + 4).dp),
modifier = Modifier
@ -318,8 +299,7 @@ class HomeViews {
it.titleName?.apply {
if (this.isNotEmpty() && (query.value.trim()
.isEmpty() || this.lowercase(Locale.getDefault())
.contains(query.value))
)
.contains(query.value))) {
GridGameItem(
it,
viewModel,
@ -328,6 +308,7 @@ class HomeViews {
selectedModel,
showError
)
}
}
}
}
@ -336,11 +317,8 @@ class HomeViews {
items(list) {
it.titleName?.apply {
if (this.isNotEmpty() && (query.value.trim()
.isEmpty() || this.lowercase(
Locale.getDefault()
)
.contains(query.value))
)
.isEmpty() || this.lowercase(Locale.getDefault())
.contains(query.value))) {
Box(modifier = Modifier.animateItemPlacement()) {
ListGameItem(
it,
@ -351,6 +329,7 @@ class HomeViews {
showError
)
}
}
}
}
}
@ -381,27 +360,27 @@ class HomeViews {
}
}
if(viewModel.mainViewModel?.loadGameModel?.value != null)
if (viewModel.mainViewModel?.loadGameModel?.value != null)
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
thread {
showLoading.value = true
val success =
viewModel.mainViewModel.loadGame(
viewModel.mainViewModel.loadGameModel.value!!,
true,
viewModel.mainViewModel.forceNceAndPptc.value
) ?: false
val success = viewModel.mainViewModel.loadGame(
viewModel.mainViewModel.loadGameModel.value!!,
true,
viewModel.mainViewModel.forceNceAndPptc.value
) ?: false
if (success == 1) {
launchOnUiThread {
viewModel.mainViewModel.navigateToGame()
}
} else {
if (success == -2)
showError.value =
"Error loading update. Please re-add update file"
showError.value = "Error loading update. Please re-add update file"
viewModel.mainViewModel.loadGameModel.value!!.close()
}
showLoading.value = false
@ -421,8 +400,9 @@ class HomeViews {
if (viewModel.mainViewModel?.selected != null) {
thread {
showLoading.value = true
val success =
viewModel.mainViewModel.loadGame(viewModel.mainViewModel.selected!!)
val success = viewModel.mainViewModel.loadGame(
viewModel.mainViewModel.selected!!
)
if (success == 1) {
launchOnUiThread {
viewModel.mainViewModel.navigateToGame()
@ -444,53 +424,54 @@ class HomeViews {
}
val showAppMenu = remember { mutableStateOf(false) }
Box {
IconButton(onClick = {
showAppMenu.value = true
}) {
Icon(
Icons.Filled.Menu,
contentDescription = "Menu"
)
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 = "Delete All Cache")
}, onClick = {
showAppMenu.value = false
viewModel.mainViewModel?.deleteCache(
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 = { 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 = "Delete All Cache") },
onClick = {
showAppMenu.value = false
viewModel.mainViewModel?.deleteCache(
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
}
)
}
}
}
@ -513,9 +494,7 @@ class HomeViews {
selectedModel: MutableState<GameModel?>,
showError: MutableState<String>
) {
remember {
selectedModel
}
remember { selectedModel }
val color =
if (selectedModel.value == gameModel) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface
@ -530,19 +509,17 @@ class HomeViews {
onClick = {
if (viewModel.mainViewModel?.selected != null) {
showAppActions.value = false
viewModel.mainViewModel.apply {
selected = null
}
viewModel.mainViewModel.apply { selected = 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 {
showLoading.value = true
val success =
viewModel.mainViewModel?.loadGame(gameModel) ?: false
val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false
if (success == 1) {
launchOnUiThread {
viewModel.mainViewModel?.navigateToGame()
}
launchOnUiThread { viewModel.mainViewModel?.navigateToGame() }
} else {
if (success == -2)
showError.value =
@ -557,7 +534,8 @@ class HomeViews {
viewModel.mainViewModel?.selected = gameModel
showAppActions.value = true
selectedModel.value = gameModel
})
}
)
) {
Row(
modifier = Modifier
@ -566,22 +544,21 @@ class HomeViews {
horizontalArrangement = Arrangement.SpaceBetween
) {
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) {
val pic = decoder.decode(gameModel.icon)
val size =
ListImageSize / Resources.getSystem().displayMetrics.density
val size = ListImageSize / Resources.getSystem().displayMetrics.density
Image(
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size)
.asImageBitmap(),
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size).asImageBitmap(),
contentDescription = gameModel.titleName + " icon",
modifier = Modifier
.padding(end = 8.dp)
.width(size.roundToInt().dp)
.height(size.roundToInt().dp)
)
} else if (gameModel.type == FileType.Nro)
NROIcon()
} else if (gameModel.type == FileType.Nro) NROIcon()
else NotAvailableIcon()
} else NotAvailableIcon()
Column {
@ -608,9 +585,7 @@ class HomeViews {
selectedModel: MutableState<GameModel?>,
showError: MutableState<String>
) {
remember {
selectedModel
}
remember { selectedModel }
val color =
if (selectedModel.value == gameModel) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface
@ -625,19 +600,17 @@ class HomeViews {
onClick = {
if (viewModel.mainViewModel?.selected != null) {
showAppActions.value = false
viewModel.mainViewModel.apply {
selected = null
}
viewModel.mainViewModel.apply { selected = 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 {
showLoading.value = true
val success =
viewModel.mainViewModel?.loadGame(gameModel) ?: false
val success = viewModel.mainViewModel?.loadGame(gameModel) ?: false
if (success == 1) {
launchOnUiThread {
viewModel.mainViewModel?.navigateToGame()
}
launchOnUiThread { viewModel.mainViewModel?.navigateToGame() }
} else {
if (success == -2)
showError.value =
@ -652,23 +625,24 @@ class HomeViews {
viewModel.mainViewModel?.selected = gameModel
showAppActions.value = true
selectedModel.value = gameModel
})
}
)
) {
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) {
val pic = decoder.decode(gameModel.icon)
Image(
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size)
.asImageBitmap(),
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size).asImageBitmap(),
contentDescription = gameModel.titleName + " icon",
modifier = Modifier
.padding(0.dp)
.clip(RoundedCornerShape(16.dp))
.align(Alignment.CenterHorizontally)
)
} else if (gameModel.type == FileType.Nro)
NROIcon()
} else if (gameModel.type == FileType.Nro) NROIcon()
else NotAvailableIcon()
} else NotAvailableIcon()
Text(
@ -708,7 +682,6 @@ class HomeViews {
.height(size.roundToInt().dp)
)
}
}
@Preview

View file

@ -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>

View file

@ -2,4 +2,5 @@
<resources>
<style name="Theme.KenjinxAndroid" parent="android:Theme.Material.Light.NoActionBar" />
</resources>