commit 5d4e2aacb1dfc682e8fa0f9adbd9183c7b023104
parent d3434a4be26c7b894ace3914c5aa12afd6f7e430
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Mon, 31 Jul 2023 00:53:55 +0200
ui(features): dialogs & translations
Diffstat:
9 files changed, 186 insertions(+), 179 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
@@ -22,8 +22,7 @@ android {
defaultConfig {
applicationId = rootProject.ext["applicationId"].toString()
minSdk = 28
- //noinspection OldTargetApi
- targetSdk = 33
+ targetSdk = 34
multiDexEnabled = true
}
@@ -37,11 +36,12 @@ android {
flavorDimensions += "abi"
productFlavors {
+
+
create("armv8") {
ndk {
abiFilters.add("arm64-v8a")
}
-
dimension = "abi"
}
@@ -81,13 +81,13 @@ android {
dependencies {
implementation(project(":core"))
implementation(libs.androidx.material.icons.extended)
- implementation(libs.androidx.navigation.compose)
- implementation(libs.androidx.activity.ktx)
implementation(libs.androidx.material3)
implementation(libs.androidx.material)
+ implementation(libs.androidx.activity.ktx)
+ implementation(libs.androidx.navigation.compose)
debugImplementation("androidx.compose.ui:ui-tooling:1.4.3")
- implementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
+ debugImplementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
implementation(kotlin("reflect"))
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt
@@ -18,159 +18,173 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import me.rhunk.snapenhance.config.ConfigProperty
import me.rhunk.snapenhance.config.impl.ConfigIntegerValue
import me.rhunk.snapenhance.config.impl.ConfigStateListValue
import me.rhunk.snapenhance.config.impl.ConfigStateSelection
-import me.rhunk.snapenhance.config.impl.ConfigStringValue
+import me.rhunk.snapenhance.manager.data.ManagerContext
-@Composable
-@Preview
-fun TestPreview() {
- KeyboardInputDialog(config = ConfigProperty.SAVE_FOLDER)
-}
+class Dialogs(
+ private val context: ManagerContext
+) {
+ @Composable
+ fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) {
+ Card(
+ shape = MaterialTheme.shapes.medium,
+ modifier = Modifier
+ .padding(10.dp, 5.dp, 10.dp, 10.dp),
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(10.dp, 10.dp, 10.dp, 10.dp)
+ .verticalScroll(ScrollState(0)),
+ ) { content() }
+ }
+ }
-@Composable
-fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) {
- Card(
- shape = MaterialTheme.shapes.medium,
- modifier = Modifier
- .padding(10.dp, 5.dp, 10.dp, 10.dp),
- ) {
- Column(
+ @Composable
+ fun DefaultEntryText(text: String, modifier: Modifier = Modifier) {
+ Text(
+ text = text,
modifier = Modifier
.padding(10.dp, 10.dp, 10.dp, 10.dp)
- .verticalScroll(ScrollState(0)),
- ) { content() }
+ .then(modifier)
+ )
}
-}
-
-@Composable
-fun DefaultEntryText(text: String, modifier: Modifier = Modifier) {
- Text(
- text = text,
- modifier = Modifier
- .padding(10.dp, 10.dp, 10.dp, 10.dp)
- .then(modifier)
- )
-}
-@Composable
-fun StateSelectionDialog(config: ConfigProperty) {
- assert(config.valueContainer is ConfigStateSelection)
- val keys = (config.valueContainer as ConfigStateSelection).keys()
- val selectedValue = remember {
- mutableStateOf(config.valueContainer.value())
- }
- DefaultDialogCard {
- keys.forEach { item ->
- fun select() {
- selectedValue.value = item
- config.valueContainer.writeFrom(item)
- }
+ @Composable
+ fun StateSelectionDialog(config: ConfigProperty) {
+ assert(config.valueContainer is ConfigStateSelection)
+ val keys = (config.valueContainer as ConfigStateSelection).keys()
+ val selectedValue = remember {
+ mutableStateOf(config.valueContainer.value())
+ }
+ DefaultDialogCard {
+ keys.forEach { item ->
+ fun select() {
+ selectedValue.value = item
+ config.valueContainer.writeFrom(item)
+ }
- Row(
- modifier = Modifier.clickable { select() },
- verticalAlignment = Alignment.CenterVertically
- ) {
- DefaultEntryText(
- text = item,
- modifier = Modifier.weight(1f)
- )
- RadioButton(
- selected = selectedValue.value == item,
- onClick = { select() }
- )
+ Row(
+ modifier = Modifier.clickable { select() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ DefaultEntryText(
+ text = if (config.disableValueLocalization)
+ item
+ else context.translation.propertyOption(config, item),
+ modifier = Modifier.weight(1f)
+ )
+ RadioButton(
+ selected = selectedValue.value == item,
+ onClick = { select() }
+ )
+ }
}
}
}
-}
-@Composable
-fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) {
- val focusRequester = remember { FocusRequester() }
+ @Composable
+ fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) {
+ val focusRequester = remember { FocusRequester() }
+
+ DefaultDialogCard {
+ val fieldValue = remember {
+ mutableStateOf(config.valueContainer.read().let {
+ TextFieldValue(
+ text = it,
+ selection = TextRange(it.length)
+ )
+ })
+ }
- DefaultDialogCard {
- val fieldValue = remember { mutableStateOf(config.valueContainer.read()) }
- TextField(
- modifier = Modifier.fillMaxWidth().padding(all = 10.dp).focusRequester(focusRequester),
- value = fieldValue.value, onValueChange = {
- fieldValue.value = it
- },
- keyboardOptions = when (config.valueContainer) {
- is ConfigIntegerValue -> {
- KeyboardOptions(keyboardType = KeyboardType.Number)
+ TextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(all = 10.dp)
+ .onGloballyPositioned {
+ focusRequester.requestFocus()
+ }
+ .focusRequester(focusRequester),
+ value = fieldValue.value,
+ onValueChange = {
+ fieldValue.value = it
+ },
+ keyboardOptions = when (config.valueContainer) {
+ is ConfigIntegerValue -> {
+ KeyboardOptions(keyboardType = KeyboardType.Number)
+ }
+ else -> {
+ KeyboardOptions(keyboardType = KeyboardType.Text)
+ }
+ },
+ singleLine = true
+ )
+
+ Row(
+ modifier = Modifier.padding(top = 10.dp).fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ ) {
+ Button(onClick = { dismiss() }) {
+ Text(text = "Cancel")
}
- else -> {
- KeyboardOptions(keyboardType = KeyboardType.Text)
+ Button(onClick = {
+ config.valueContainer.writeFrom(fieldValue.value.text)
+ dismiss()
+ }) {
+ Text(text = "Ok")
}
- },
- singleLine = true
- )
-
- Row(
- modifier = Modifier.padding(top = 10.dp).fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceEvenly,
- ) {
- Button(onClick = { dismiss() }) {
- Text(text = "Cancel")
- }
- Button(onClick = {
- config.valueContainer.writeFrom(fieldValue.value)
- dismiss()
- }) {
- Text(text = "Ok")
}
}
}
- LaunchedEffect(Unit) {
- focusRequester.requestFocus()
- }
-}
-
-@Composable
-fun StateListDialog(config: ConfigProperty) {
- assert(config.valueContainer is ConfigStateListValue)
- val stateList = (config.valueContainer as ConfigStateListValue).value()
- DefaultDialogCard {
- stateList.keys.forEach { key ->
- val state = remember {
- mutableStateOf(stateList[key] ?: false)
- }
+ @Composable
+ fun StateListDialog(config: ConfigProperty) {
+ assert(config.valueContainer is ConfigStateListValue)
+ val stateList = (config.valueContainer as ConfigStateListValue).value()
+ DefaultDialogCard {
+ stateList.keys.forEach { key ->
+ val state = remember {
+ mutableStateOf(stateList[key] ?: false)
+ }
- fun toggle(value: Boolean? = null) {
- state.value = value ?: !state.value
- stateList[key] = state.value
- }
+ fun toggle(value: Boolean? = null) {
+ state.value = value ?: !state.value
+ stateList[key] = state.value
+ }
- Row(
- modifier = Modifier.clickable { toggle() },
- verticalAlignment = Alignment.CenterVertically
- ) {
- DefaultEntryText(
- text = key,
- modifier = Modifier
- .weight(1f)
- )
- Switch(
- checked = state.value,
- onCheckedChange = {
- toggle(it)
- }
- )
+ Row(
+ modifier = Modifier.clickable { toggle() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ DefaultEntryText(
+ text = if (config.disableValueLocalization)
+ key
+ else context.translation.propertyOption(config, key),
+ modifier = Modifier
+ .weight(1f)
+ )
+ Switch(
+ checked = state.value,
+ onCheckedChange = {
+ toggle(it)
+ }
+ )
+ }
}
}
}
-}-
\ No newline at end of file
+}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt
@@ -4,22 +4,8 @@ import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.Text
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Settings
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.TopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
import me.rhunk.snapenhance.manager.data.ManagerContext
@@ -27,26 +13,20 @@ class MainActivity : ComponentActivity() {
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val startDestination = intent.getStringExtra("route")?.let { EnumSection.fromRoute(it) } ?: EnumSection.HOME
val managerContext = ManagerContext(this)
setContent {
- App(managerContext)
+ val navController = rememberNavController()
+ val navigation = Navigation(managerContext)
+ AppMaterialTheme {
+ Scaffold(
+ containerColor = MaterialTheme.colorScheme.background,
+ bottomBar = { navigation.NavBar(navController = navController) }
+ ) { innerPadding ->
+ navigation.NavigationHost(navController = navController, innerPadding = innerPadding, startDestination = startDestination)
+ }
+ }
}
}
}
-
-@Composable
-fun App(
- context: ManagerContext
-) {
- val navController = rememberNavController()
- val navigation = Navigation(context)
- AppMaterialTheme {
- Scaffold(
- containerColor = MaterialTheme.colorScheme.background,
- bottomBar = { navigation.NavBar(navController = navController) }
- ) { innerPadding ->
- navigation.NavigationHost(navController = navController, innerPadding = innerPadding)
- }
- }
-}-
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt
@@ -30,6 +30,7 @@ class Navigation(
) {
@Composable
fun NavigationHost(
+ startDestination: EnumSection,
navController: NavHostController,
innerPadding: PaddingValues
) {
@@ -40,7 +41,7 @@ class Navigation(
instance.navController = navController
} }
- NavHost(navController, startDestination = EnumSection.FEATURES.route, Modifier.padding(innerPadding)) {
+ NavHost(navController, startDestination = startDestination.route, Modifier.padding(innerPadding)) {
sections.forEach { (section, instance) ->
composable(section.route) {
instance.Content()
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt
@@ -48,6 +48,12 @@ enum class EnumSection(
title = "Debug",
icon = Icons.Filled.BugReport
);
+
+ companion object {
+ fun fromRoute(route: String): EnumSection {
+ return values().first { it.route == route }
+ }
+ }
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt
@@ -4,9 +4,12 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -31,6 +34,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -43,15 +47,15 @@ import me.rhunk.snapenhance.config.impl.ConfigStateListValue
import me.rhunk.snapenhance.config.impl.ConfigStateSelection
import me.rhunk.snapenhance.config.impl.ConfigStateValue
import me.rhunk.snapenhance.config.impl.ConfigStringValue
-import me.rhunk.snapenhance.manager.StateListDialog
+import me.rhunk.snapenhance.manager.Dialogs
import me.rhunk.snapenhance.manager.Section
-import me.rhunk.snapenhance.manager.StateSelectionDialog
-import me.rhunk.snapenhance.manager.KeyboardInputDialog
typealias ClickCallback = (Boolean) -> Unit
typealias RegisterClickCallback = (ClickCallback) -> ClickCallback
class FeaturesSection : Section() {
+ private val dialogs by lazy { Dialogs(manager) }
+
@Composable
private fun PropertyAction(item: ConfigProperty, registerClickCallback: RegisterClickCallback) {
val showDialog = remember { mutableStateOf(false) }
@@ -82,12 +86,15 @@ class FeaturesSection : Section() {
is ConfigStateSelection -> {
registerDialogOnClickCallback()
dialogComposable.value = {
- StateSelectionDialog(item)
+ dialogs.StateSelectionDialog(item)
}
Text(
- text = container.value().let {
- it.substring(0, it.length.coerceAtMost(20))
- }
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ modifier = Modifier.widthIn(0.dp, 120.dp),
+ text = if (item.disableValueLocalization) container.value() else {
+ manager.translation.propertyOption(item, container.value())
+ },
)
}
@@ -95,10 +102,10 @@ class FeaturesSection : Section() {
dialogComposable.value = {
when (container) {
is ConfigStateListValue -> {
- StateListDialog(item)
+ dialogs.StateListDialog(item)
}
is ConfigStringValue, is ConfigIntegerValue -> {
- KeyboardInputDialog(item) { showDialog.value = false }
+ dialogs.KeyboardInputDialog(item) { showDialog.value = false }
}
}
}
@@ -106,7 +113,7 @@ class FeaturesSection : Section() {
registerDialogOnClickCallback().let { { it.invoke(true) } }.also {
if (container is ConfigIntegerValue) {
FilledIconButton(onClick = it) {
- Text(text = container.value().toString())
+ Text(text = container.value().toString(), modifier = Modifier.wrapContentWidth(), overflow = TextOverflow.Ellipsis)
}
} else {
IconButton(onClick = it) {
@@ -132,7 +139,7 @@ class FeaturesSection : Section() {
Row(
modifier = Modifier
.fillMaxSize()
- .padding(all = 10.dp),
+ .padding(all = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
@@ -206,10 +213,11 @@ class FeaturesSection : Section() {
)
LazyColumn(
modifier = Modifier
- .fillMaxSize(),
+ .fillMaxHeight(),
verticalArrangement = Arrangement.Center
) {
items(configItems) { item ->
+ if (item.shouldAppearInSettings.not()) return@items
PropertyCard(item)
}
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt
@@ -1,13 +1,8 @@
package me.rhunk.snapenhance.manager.sections
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
import me.rhunk.snapenhance.manager.Section
class NotImplemented : Section() {
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt
@@ -88,6 +88,10 @@ class TranslationWrapper {
return get("property.${property.translationKey}.description")
}
+ fun propertyOption(property: ConfigProperty, item: String): String {
+ return get(property.getOptionTranslationKey(item))
+ }
+
fun format(key: String, vararg args: Pair<String, String>): String {
return args.fold(get(key)) { acc, pair ->
acc.replace("{${pair.first}}", pair.second)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt
@@ -48,9 +48,10 @@ class SettingsGearInjector : AbstractMenu() {
setOnClickListener {
val intent = Intent().apply {
- setClassName(BuildConfig.APPLICATION_ID, ConfigActivity::class.java.name)
+ setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.manager.MainActivity")
+ putExtra("route", "features")
+ putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists())
}
- intent.putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists())
context.startActivity(intent)
}