ui(features): dialogs & translations

This commit is contained in:
rhunk
2023-07-31 00:53:55 +02:00
parent d3434a4be2
commit 5d4e2aacb1
9 changed files with 190 additions and 181 deletions

View File

@ -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"))
}

View File

@ -18,31 +18,29 @@ 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)
}
@Composable
fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) {
class Dialogs(
private val context: ManagerContext
) {
@Composable
fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) {
Card(
shape = MaterialTheme.shapes.medium,
modifier = Modifier
@ -54,20 +52,20 @@ fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) {
.verticalScroll(ScrollState(0)),
) { content() }
}
}
}
@Composable
fun DefaultEntryText(text: String, modifier: Modifier = 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) {
@Composable
fun StateSelectionDialog(config: ConfigProperty) {
assert(config.valueContainer is ConfigStateSelection)
val keys = (config.valueContainer as ConfigStateSelection).keys()
val selectedValue = remember {
@ -85,7 +83,9 @@ fun StateSelectionDialog(config: ConfigProperty) {
verticalAlignment = Alignment.CenterVertically
) {
DefaultEntryText(
text = item,
text = if (config.disableValueLocalization)
item
else context.translation.propertyOption(config, item),
modifier = Modifier.weight(1f)
)
RadioButton(
@ -95,17 +95,32 @@ fun StateSelectionDialog(config: ConfigProperty) {
}
}
}
}
}
@Composable
fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) {
@Composable
fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) {
val focusRequester = remember { FocusRequester() }
DefaultDialogCard {
val fieldValue = remember { mutableStateOf(config.valueContainer.read()) }
val fieldValue = remember {
mutableStateOf(config.valueContainer.read().let {
TextFieldValue(
text = it,
selection = TextRange(it.length)
)
})
}
TextField(
modifier = Modifier.fillMaxWidth().padding(all = 10.dp).focusRequester(focusRequester),
value = fieldValue.value, onValueChange = {
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp)
.onGloballyPositioned {
focusRequester.requestFocus()
}
.focusRequester(focusRequester),
value = fieldValue.value,
onValueChange = {
fieldValue.value = it
},
keyboardOptions = when (config.valueContainer) {
@ -127,21 +142,17 @@ fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) {
Text(text = "Cancel")
}
Button(onClick = {
config.valueContainer.writeFrom(fieldValue.value)
config.valueContainer.writeFrom(fieldValue.value.text)
dismiss()
}) {
Text(text = "Ok")
}
}
}
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
}
@Composable
fun StateListDialog(config: ConfigProperty) {
@Composable
fun StateListDialog(config: ConfigProperty) {
assert(config.valueContainer is ConfigStateListValue)
val stateList = (config.valueContainer as ConfigStateListValue).value()
DefaultDialogCard {
@ -160,7 +171,9 @@ fun StateListDialog(config: ConfigProperty) {
verticalAlignment = Alignment.CenterVertically
) {
DefaultEntryText(
text = key,
text = if (config.disableValueLocalization)
key
else context.translation.propertyOption(config, key),
modifier = Modifier
.weight(1f)
)
@ -173,4 +186,5 @@ fun StateListDialog(config: ConfigProperty) {
}
}
}
}
}

View File

@ -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)
}
}
}
@Composable
fun App(
context: ManagerContext
) {
val navController = rememberNavController()
val navigation = Navigation(context)
val navigation = Navigation(managerContext)
AppMaterialTheme {
Scaffold(
containerColor = MaterialTheme.colorScheme.background,
bottomBar = { navigation.NavBar(navController = navController) }
) { innerPadding ->
navigation.NavigationHost(navController = navController, innerPadding = innerPadding)
navigation.NavigationHost(navController = navController, innerPadding = innerPadding, startDestination = startDestination)
}
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -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() {

View File

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

View File

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