feat: patch options screen previews & improve layout
This commit is contained in:
parent
f988846af2
commit
713d308f3e
6 changed files with 252 additions and 111 deletions
|
|
@ -0,0 +1,95 @@
|
|||
package com.aliucord.manager.ui.previews.screens
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.*
|
||||
import com.aliucord.manager.ui.components.ManagerTheme
|
||||
import com.aliucord.manager.ui.screens.patchopts.*
|
||||
import com.aliucord.manager.ui.util.DiscordVersion
|
||||
|
||||
// This preview has scrollable/interactable content that cannot be tested from an IDE preview
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun PatchOptionsScreenPreview(
|
||||
@PreviewParameter(ContributorsProvider::class)
|
||||
parameters: PatchOptionsParameters,
|
||||
) {
|
||||
ManagerTheme {
|
||||
PatchOptionsScreenContent(
|
||||
isUpdate = parameters.isUpdate,
|
||||
isDevMode = parameters.isDevMode,
|
||||
supportedVersion = parameters.supportedVersion,
|
||||
debuggable = parameters.debuggable,
|
||||
setDebuggable = {},
|
||||
replaceIcon = parameters.replaceIcon,
|
||||
setReplaceIcon = {},
|
||||
appName = parameters.appName,
|
||||
appNameIsError = parameters.appNameIsError,
|
||||
setAppName = {},
|
||||
packageName = parameters.packageName,
|
||||
packageNameState = parameters.packageNameState,
|
||||
setPackageName = {},
|
||||
isConfigValid = parameters.isConfigValid,
|
||||
onInstall = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class PatchOptionsParameters(
|
||||
val isUpdate: Boolean,
|
||||
val isDevMode: Boolean,
|
||||
val supportedVersion: DiscordVersion,
|
||||
val debuggable: Boolean,
|
||||
val replaceIcon: Boolean,
|
||||
val appName: String,
|
||||
val appNameIsError: Boolean,
|
||||
val packageName: String,
|
||||
val packageNameState: PackageNameState,
|
||||
val isConfigValid: Boolean,
|
||||
)
|
||||
|
||||
private class ContributorsProvider : PreviewParameterProvider<PatchOptionsParameters> {
|
||||
private val stableVersion = DiscordVersion.Existing(DiscordVersion.Type.STABLE, "126.21", 126021)
|
||||
|
||||
override val values = sequenceOf(
|
||||
// Default initial install
|
||||
PatchOptionsParameters(
|
||||
isUpdate = false,
|
||||
isDevMode = false,
|
||||
supportedVersion = stableVersion,
|
||||
debuggable = false,
|
||||
replaceIcon = true,
|
||||
appName = PatchOptions.Default.appName,
|
||||
appNameIsError = false,
|
||||
packageName = PatchOptions.Default.packageName,
|
||||
packageNameState = PackageNameState.Ok,
|
||||
isConfigValid = true,
|
||||
),
|
||||
PatchOptionsParameters(
|
||||
isUpdate = true,
|
||||
isDevMode = false,
|
||||
supportedVersion = stableVersion,
|
||||
debuggable = false,
|
||||
replaceIcon = true,
|
||||
appName = "an invalid app name.",
|
||||
appNameIsError = true,
|
||||
packageName = "a b",
|
||||
packageNameState = PackageNameState.Invalid,
|
||||
isConfigValid = false,
|
||||
),
|
||||
PatchOptionsParameters(
|
||||
isUpdate = false,
|
||||
isDevMode = true,
|
||||
supportedVersion = stableVersion,
|
||||
debuggable = true,
|
||||
replaceIcon = false,
|
||||
appName = PatchOptions.Default.appName,
|
||||
appNameIsError = false,
|
||||
packageName = PatchOptions.Default.packageName,
|
||||
packageNameState = PackageNameState.Taken,
|
||||
isConfigValid = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -24,19 +24,11 @@ class PatchOptionsModel(
|
|||
var packageNameState by mutableStateOf(PackageNameState.Ok)
|
||||
private set
|
||||
|
||||
val packageNameIsDefault by derivedStateOf {
|
||||
packageName == PatchOptions.Default.packageName
|
||||
}
|
||||
|
||||
fun changePackageName(newPackageName: String) {
|
||||
packageName = newPackageName
|
||||
fetchPkgNameStateDebounced()
|
||||
}
|
||||
|
||||
fun resetPackageName() {
|
||||
changePackageName(PatchOptions.Default.packageName)
|
||||
}
|
||||
|
||||
// ---------- App name state ----------
|
||||
var appName by mutableStateOf(prefilledOptions.appName)
|
||||
private set
|
||||
|
|
@ -44,19 +36,11 @@ class PatchOptionsModel(
|
|||
var appNameIsError by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
val appNameIsDefault by derivedStateOf {
|
||||
appName == PatchOptions.Default.appName
|
||||
}
|
||||
|
||||
fun changeAppName(newAppName: String) {
|
||||
appName = newAppName
|
||||
appNameIsError = newAppName.length !in (1..150)
|
||||
}
|
||||
|
||||
fun resetAppName() {
|
||||
appName = PatchOptions.Default.appName
|
||||
}
|
||||
|
||||
// ---------- Icon patching state ----------
|
||||
var replaceIcon by mutableStateOf(prefilledOptions.iconReplacement == PatchOptions.IconReplacement.Aliucord)
|
||||
private set
|
||||
|
|
|
|||
|
|
@ -6,25 +6,28 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.AnimatedVersionDisplay
|
||||
import com.aliucord.manager.ui.components.TextDivider
|
||||
import com.aliucord.manager.ui.components.VersionDisplay
|
||||
import com.aliucord.manager.ui.components.dialogs.NetworkWarningDialog
|
||||
import com.aliucord.manager.ui.components.dialogs.UnknownSourcesPermissionDialog
|
||||
import com.aliucord.manager.ui.screens.patching.PatchingScreen
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.PackageNameState
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.PackageNameStateLabel
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.PatchOptionsAppBar
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.options.SwitchPatchOption
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.options.TextPatchOption
|
||||
|
|
@ -48,7 +51,7 @@ class PatchOptionsScreen(
|
|||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val model = getScreenModel<PatchOptionsModel>() { parametersOf(prefilledOptions ?: PatchOptions.Default) }
|
||||
val model = getScreenModel<PatchOptionsModel> { parametersOf(prefilledOptions ?: PatchOptions.Default) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
// Ensure that when popping this screen off the stack that permission requests don't get triggered
|
||||
|
|
@ -74,100 +77,160 @@ class PatchOptionsScreen(
|
|||
|
||||
UnknownSourcesPermissionDialog()
|
||||
|
||||
Scaffold(
|
||||
topBar = { PatchOptionsAppBar(isUpdate = prefilledOptions != null) },
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedByLastAtBottom(20.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = 20.dp, vertical = 10.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_title),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
PatchOptionsScreenContent(
|
||||
isUpdate = prefilledOptions != null,
|
||||
isDevMode = model.isDevMode,
|
||||
supportedVersion = supportedVersion,
|
||||
|
||||
var animatedVersion by rememberSaveable { mutableStateOf<DiscordVersion>(DiscordVersion.None) }
|
||||
LaunchedEffect(Unit) {
|
||||
animatedVersion = supportedVersion
|
||||
debuggable = model.debuggable,
|
||||
setDebuggable = model::changeDebuggable,
|
||||
|
||||
replaceIcon = model.replaceIcon,
|
||||
setReplaceIcon = model::changeReplaceIcon,
|
||||
|
||||
appName = model.appName,
|
||||
appNameIsError = model.appNameIsError,
|
||||
setAppName = model::changeAppName,
|
||||
|
||||
packageName = model.packageName,
|
||||
packageNameState = model.packageNameState,
|
||||
setPackageName = model::changePackageName,
|
||||
|
||||
isConfigValid = model.isConfigValid,
|
||||
onInstall = { navigator.push(PatchingScreen(model.generateConfig())) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PatchOptionsScreenContent(
|
||||
isUpdate: Boolean,
|
||||
isDevMode: Boolean,
|
||||
supportedVersion: DiscordVersion,
|
||||
|
||||
debuggable: Boolean,
|
||||
setDebuggable: (Boolean) -> Unit,
|
||||
|
||||
replaceIcon: Boolean,
|
||||
setReplaceIcon: (Boolean) -> Unit,
|
||||
|
||||
appName: String,
|
||||
appNameIsError: Boolean,
|
||||
setAppName: (String) -> Unit,
|
||||
|
||||
packageName: String,
|
||||
packageNameState: PackageNameState,
|
||||
setPackageName: (String) -> Unit,
|
||||
|
||||
isConfigValid: Boolean,
|
||||
onInstall: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = { PatchOptionsAppBar(isUpdate = isUpdate) },
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedByLastAtBottom(20.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = 20.dp, vertical = 10.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_title),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
TextDivider(text = stringResource(R.string.patchopts_divider_basic))
|
||||
|
||||
SwitchPatchOption(
|
||||
icon = painterResource(R.drawable.ic_app_shortcut),
|
||||
name = stringResource(R.string.patchopts_icon_title),
|
||||
description = stringResource(R.string.patchopts_icon_desc),
|
||||
value = replaceIcon,
|
||||
onValueChange = setReplaceIcon,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
|
||||
val appNameIsDefault by remember {
|
||||
derivedStateOf {
|
||||
appName == PatchOptions.Default.appName
|
||||
}
|
||||
AnimatedVersionDisplay(
|
||||
version = animatedVersion,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.offset(y = (-10).dp),
|
||||
)
|
||||
}
|
||||
TextPatchOption(
|
||||
name = stringResource(R.string.patchopts_appname_title),
|
||||
description = stringResource(R.string.patchopts_appname_desc),
|
||||
value = appName,
|
||||
valueIsError = appNameIsError,
|
||||
valueIsDefault = appNameIsDefault,
|
||||
onValueChange = setAppName,
|
||||
onValueReset = { setAppName(PatchOptions.Default.appName) },
|
||||
)
|
||||
|
||||
TextDivider(text = stringResource(R.string.patchopts_divider_basic))
|
||||
val packageNameIsDefault by remember {
|
||||
derivedStateOf {
|
||||
packageName == PatchOptions.Default.packageName
|
||||
}
|
||||
}
|
||||
TextPatchOption(
|
||||
name = stringResource(R.string.patchopts_pkgname_title),
|
||||
description = stringResource(R.string.patchopts_pkgname_desc),
|
||||
value = packageName,
|
||||
valueIsError = packageNameState == PackageNameState.Invalid,
|
||||
valueIsDefault = packageNameIsDefault,
|
||||
onValueChange = setPackageName,
|
||||
onValueReset = { setPackageName(PatchOptions.Default.packageName) },
|
||||
) {
|
||||
PackageNameStateLabel(
|
||||
state = packageNameState,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
if (isDevMode) {
|
||||
TextDivider(
|
||||
text = stringResource(R.string.patchopts_divider_advanced),
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
|
||||
SwitchPatchOption(
|
||||
icon = painterResource(R.drawable.ic_app_shortcut),
|
||||
name = stringResource(R.string.patchopts_icon_title),
|
||||
description = stringResource(R.string.patchopts_icon_desc),
|
||||
value = model.replaceIcon,
|
||||
onValueChange = model::changeReplaceIcon,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
icon = painterResource(R.drawable.ic_bug),
|
||||
name = stringResource(R.string.patchopts_debuggable_title),
|
||||
description = stringResource(R.string.patchopts_debuggable_desc),
|
||||
value = debuggable,
|
||||
onValueChange = setDebuggable,
|
||||
)
|
||||
}
|
||||
|
||||
TextPatchOption(
|
||||
name = stringResource(R.string.patchopts_appname_title),
|
||||
description = stringResource(R.string.patchopts_appname_desc),
|
||||
value = model.appName,
|
||||
valueIsError = model.appNameIsError,
|
||||
valueIsDefault = model.appNameIsDefault,
|
||||
onValueChange = model::changeAppName,
|
||||
onValueReset = model::resetAppName
|
||||
)
|
||||
VersionDisplay(
|
||||
version = supportedVersion,
|
||||
prefix = {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(stringResource(R.string.version_supported))
|
||||
append(" ")
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.alpha(.5f)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(top = 20.dp)
|
||||
)
|
||||
|
||||
TextPatchOption(
|
||||
name = stringResource(R.string.patchopts_pkgname_title),
|
||||
description = stringResource(R.string.patchopts_pkgname_desc),
|
||||
value = model.packageName,
|
||||
valueIsError = model.packageNameState == PackageNameState.Invalid,
|
||||
valueIsDefault = model.packageNameIsDefault,
|
||||
onValueChange = model::changePackageName,
|
||||
onValueReset = model::resetPackageName,
|
||||
) {
|
||||
PackageNameState(
|
||||
state = model.packageNameState,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
if (model.isDevMode) {
|
||||
TextDivider(
|
||||
text = stringResource(R.string.patchopts_divider_advanced),
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
|
||||
SwitchPatchOption(
|
||||
icon = painterResource(R.drawable.ic_bug),
|
||||
name = stringResource(R.string.patchopts_debuggable_title),
|
||||
description = stringResource(R.string.patchopts_debuggable_desc),
|
||||
value = model.debuggable,
|
||||
onValueChange = model::changeDebuggable,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
FilledTonalButton(
|
||||
enabled = model.isConfigValid,
|
||||
onClick = { navigator.push(PatchingScreen(model.generateConfig())) },
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(bottom = 10.dp)
|
||||
.align(Alignment.End),
|
||||
) {
|
||||
Text(stringResource(R.string.action_install))
|
||||
}
|
||||
FilledTonalButton(
|
||||
enabled = isConfigValid,
|
||||
onClick = onInstall,
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(bottom = 10.dp)
|
||||
.align(Alignment.End),
|
||||
) {
|
||||
Text(stringResource(R.string.action_install))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.aliucord.manager.R
|
|||
import com.aliucord.manager.ui.screens.patchopts.PackageNameState
|
||||
|
||||
@Composable
|
||||
fun PackageNameState(
|
||||
fun PackageNameStateLabel(
|
||||
state: PackageNameState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.BackButton
|
||||
import com.aliucord.manager.ui.screens.settings.SettingsScreen
|
||||
|
|
@ -18,9 +17,9 @@ fun PatchOptionsAppBar(
|
|||
navigationIcon = { BackButton() },
|
||||
title = { Text(stringResource(if (!isUpdate) R.string.action_add_install else R.string.action_update_install)) },
|
||||
actions = {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val navigator = LocalNavigator.current
|
||||
|
||||
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
||||
IconButton(onClick = { navigator?.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_settings),
|
||||
contentDescription = stringResource(R.string.navigation_settings)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
<string name="action_dismiss">Dismiss</string>
|
||||
<string name="action_install">Install</string>
|
||||
<string name="action_uninstall">Uninstall</string>
|
||||
<string name="action_add_install">New Aliucord Installation</string>
|
||||
<string name="action_update_install">Update Aliucord Installation</string>
|
||||
<string name="action_add_install">New install</string>
|
||||
<string name="action_update_install">Update install</string>
|
||||
<string name="action_update">Update</string>
|
||||
<string name="action_clear">Clear</string>
|
||||
<string name="action_launch">Launch</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue