mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-08-27 14:37:45 +10:00
Custom Patch Selection
This commit is contained in:
+3
@@ -34,6 +34,9 @@ private fun PatchOptionsScreenPreview(
|
||||
onSelectCustomInjector = {},
|
||||
customPatches = parameters.customPatches,
|
||||
onSelectCustomPatches = {},
|
||||
enabledPatchCount = KnownPatch.All.size,
|
||||
isPatchEnabled = { true },
|
||||
onTogglePatch = { _, _ -> },
|
||||
isConfigValid = parameters.isConfigValid,
|
||||
onInstall = {},
|
||||
)
|
||||
|
||||
+2
-2
@@ -87,9 +87,9 @@ class PatchingScreenModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun clearCache() = screenModelScope.launchIO {
|
||||
fun clearCache() {
|
||||
paths.clearCache()
|
||||
mainThread { application.showToast(R.string.action_cleared_cache) }
|
||||
application.showToast(R.string.action_cleared_cache)
|
||||
}
|
||||
|
||||
fun getCurrentInstallId(): String? = installId
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
enum class KnownPatch(
|
||||
val fileNames: List<String>,
|
||||
@StringRes val titleRes: Int,
|
||||
@StringRes val descRes: Int,
|
||||
val requires: List<KnownPatch> = emptyList(),
|
||||
) {
|
||||
LyricsDisableCover(
|
||||
fileNames = listOf("lyrics-disable-cover.patch"),
|
||||
titleRes = R.string.patch_lyrics_disable_cover_title,
|
||||
descRes = R.string.patch_lyrics_disable_cover_desc,
|
||||
),
|
||||
LyricsProgressPill(
|
||||
fileNames = listOf(
|
||||
"lyrics-progress-pill.patch",
|
||||
"lyrics-fade-region.patch",
|
||||
"lyrics-active-line-only.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_progress_pill_title,
|
||||
descRes = R.string.patch_lyrics_progress_pill_desc,
|
||||
requires = listOf(LyricsDisableCover),
|
||||
),
|
||||
LyricsReplaceLyricButton(
|
||||
fileNames = listOf(
|
||||
"lyrics-replace-lyric-button-1-remove.patch",
|
||||
"lyrics-replace-lyric-button-2-sparkle.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_replace_button_title,
|
||||
descRes = R.string.patch_lyrics_replace_button_desc,
|
||||
),
|
||||
PlayerBackdrop(
|
||||
fileNames = listOf("player-backdrop.patch"),
|
||||
titleRes = R.string.patch_player_backdrop_title,
|
||||
descRes = R.string.patch_player_backdrop_desc,
|
||||
);
|
||||
|
||||
companion object {
|
||||
val All: List<KnownPatch> = entries.sortedBy { it.fileNames.first() }
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ data class PatchOptions(
|
||||
* A custom smali patches bundle that was used rather than the latest.
|
||||
*/
|
||||
val customPatches: PatchComponent? = null,
|
||||
|
||||
val disabledPatches: Set<String> = emptySet(),
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
val Default = PatchOptions(
|
||||
@@ -42,6 +44,7 @@ data class PatchOptions(
|
||||
debuggable = false,
|
||||
customInjector = null,
|
||||
customPatches = null,
|
||||
disabledPatches = emptySet(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+40
@@ -50,6 +50,45 @@ class PatchOptionsModel(
|
||||
debuggable = value
|
||||
}
|
||||
|
||||
// ---------- Patch selection state ----------
|
||||
var disabledPatches by mutableStateOf(prefilledOptions.disabledPatches)
|
||||
private set
|
||||
|
||||
fun isPatchEnabled(patch: KnownPatch): Boolean =
|
||||
patch.fileNames.none { it in disabledPatches }
|
||||
|
||||
fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) {
|
||||
val units = if (enabled) {
|
||||
buildSet {
|
||||
fun addWithDeps(p: KnownPatch) {
|
||||
if (add(p)) p.requires.forEach(::addWithDeps)
|
||||
}
|
||||
addWithDeps(patch)
|
||||
}
|
||||
} else {
|
||||
buildSet {
|
||||
fun addWithDependents(p: KnownPatch) {
|
||||
if (add(p)) {
|
||||
KnownPatch.All
|
||||
.filter { p in it.requires }
|
||||
.forEach(::addWithDependents)
|
||||
}
|
||||
}
|
||||
addWithDependents(patch)
|
||||
}
|
||||
}
|
||||
|
||||
val affectedFiles = units.flatMap { it.fileNames }.toSet()
|
||||
disabledPatches = if (enabled) {
|
||||
disabledPatches - affectedFiles
|
||||
} else {
|
||||
disabledPatches + affectedFiles
|
||||
}
|
||||
}
|
||||
|
||||
val enabledPatchCount: Int
|
||||
get() = KnownPatch.All.count { isPatchEnabled(it) }
|
||||
|
||||
// ---------- Custom components state ----------
|
||||
var customInjector by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
@@ -93,6 +132,7 @@ class PatchOptionsModel(
|
||||
debuggable = debuggable,
|
||||
customInjector = customInjector,
|
||||
customPatches = customPatches,
|
||||
disabledPatches = disabledPatches,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+17
@@ -21,6 +21,7 @@ import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||
import com.meowarex.rlmobile.ui.screens.patching.PatchingScreen
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PackageNameStateLabel
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PatchOptionsAppBar
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PatchSelectionAccordion
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.options.*
|
||||
import com.meowarex.rlmobile.ui.util.spacedByLastAtBottom
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
@@ -59,6 +60,10 @@ class PatchOptionsScreen(
|
||||
onSelectCustomInjector = { model.selectCustomInjector(navigator) },
|
||||
onSelectCustomPatches = { model.selectCustomPatches(navigator) },
|
||||
|
||||
enabledPatchCount = model.enabledPatchCount,
|
||||
isPatchEnabled = model::isPatchEnabled,
|
||||
onTogglePatch = model::setPatchEnabled,
|
||||
|
||||
isConfigValid = model.isConfigValid,
|
||||
onInstall = {
|
||||
navigator.push(PatchingScreen(model.generateConfig()))
|
||||
@@ -88,6 +93,10 @@ fun PatchOptionsScreenContent(
|
||||
customPatches: PatchComponent?,
|
||||
onSelectCustomPatches: () -> Unit,
|
||||
|
||||
enabledPatchCount: Int,
|
||||
isPatchEnabled: (KnownPatch) -> Boolean,
|
||||
onTogglePatch: (KnownPatch, Boolean) -> Unit,
|
||||
|
||||
isConfigValid: Boolean,
|
||||
onInstall: () -> Unit,
|
||||
) {
|
||||
@@ -148,6 +157,14 @@ fun PatchOptionsScreenContent(
|
||||
}
|
||||
}
|
||||
|
||||
PatchSelectionAccordion(
|
||||
enabledCount = enabledPatchCount,
|
||||
totalCount = KnownPatch.All.size,
|
||||
isEnabled = isPatchEnabled,
|
||||
onToggle = onTogglePatch,
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
)
|
||||
|
||||
if (isDevMode) {
|
||||
TextDivider(
|
||||
text = stringResource(R.string.patchopts_divider_advanced),
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.KnownPatch
|
||||
|
||||
@Composable
|
||||
fun PatchSelectionAccordion(
|
||||
enabledCount: Int,
|
||||
totalCount: Int,
|
||||
isEnabled: (KnownPatch) -> Boolean,
|
||||
onToggle: (KnownPatch, Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
val rotation by animateFloatAsState(
|
||||
targetValue = if (expanded) 180f else 0f,
|
||||
label = "patch-accordion-arrow",
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.large)
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerLow),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(role = Role.Button) { expanded = !expanded }
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_patches_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.patchopts_patches_summary,
|
||||
enabledCount,
|
||||
totalCount,
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_arrow_down_small),
|
||||
contentDescription = stringResource(
|
||||
if (expanded) R.string.action_collapse else R.string.action_expand,
|
||||
),
|
||||
modifier = Modifier.rotate(rotation),
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background.copy(0.4f))
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_patches_desc),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.alpha(.7f)
|
||||
.padding(bottom = 8.dp),
|
||||
)
|
||||
|
||||
for (patch in KnownPatch.All) key(patch) {
|
||||
PatchCheckboxRow(
|
||||
title = stringResource(patch.titleRes),
|
||||
description = stringResource(patch.descRes),
|
||||
checked = isEnabled(patch),
|
||||
onCheckedChange = { onToggle(patch, it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PatchCheckboxRow(
|
||||
title: String,
|
||||
description: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
val interactionSource = remember(::MutableInteractionSource)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
role = Role.Checkbox,
|
||||
) { onCheckedChange(!checked) }
|
||||
.padding(vertical = 4.dp),
|
||||
) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -64,13 +64,10 @@ class SettingsModel(
|
||||
if (value) UpdateCheckWorker.schedule(application) else UpdateCheckWorker.cancel(application)
|
||||
}
|
||||
|
||||
fun clearCache() = screenModelScope.launchIO {
|
||||
fun clearCache() {
|
||||
paths.clearCache()
|
||||
|
||||
mainThread {
|
||||
patchedApkExists = false
|
||||
application.showToast(R.string.action_cleared_cache)
|
||||
}
|
||||
patchedApkExists = false
|
||||
application.showToast(R.string.action_cleared_cache)
|
||||
}
|
||||
|
||||
fun copyInstallInfo() {
|
||||
|
||||
Reference in New Issue
Block a user