mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-26 17:47:27 +00:00
upgrade user theme selection, now multiple themes are possible
currently missing: old "show more/all colors", and import/export
This commit is contained in:
parent
40433bd8d2
commit
63bda02cc4
13 changed files with 430 additions and 109 deletions
|
@ -14,7 +14,7 @@ android {
|
|||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 2302
|
||||
versionName = "2.3"
|
||||
versionName = "2.3+dev1"
|
||||
ndk {
|
||||
abiFilters.clear()
|
||||
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
|
||||
|
|
|
@ -24,6 +24,7 @@ import helium314.keyboard.latin.utils.brightenOrDarken
|
|||
import helium314.keyboard.latin.utils.isBrightColor
|
||||
import helium314.keyboard.latin.utils.isGoodContrast
|
||||
import helium314.keyboard.latin.utils.prefs
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.EnumMap
|
||||
|
||||
|
@ -59,19 +60,23 @@ private constructor(val themeId: Int, @JvmField val mStyleId: Int) {
|
|||
const val THEME_PINK = "pink"
|
||||
const val THEME_SAND = "sand"
|
||||
const val THEME_VIOLETTE = "violette"
|
||||
fun getAvailableColors(prefs: SharedPreferences, isNight: Boolean) = listOfNotNull(
|
||||
if (!isNight) THEME_LIGHT else null, THEME_DARK, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) THEME_DYNAMIC else null,
|
||||
if (prefs.getString(Settings.PREF_THEME_STYLE, Defaults.PREF_THEME_STYLE) == STYLE_HOLO) THEME_HOLO_WHITE else null,
|
||||
THEME_DARKER, THEME_BLACK, if (!isNight) THEME_BLUE_GRAY else null, if (!isNight) THEME_BROWN else null,
|
||||
THEME_CHOCOLATE, THEME_CLOUDY, THEME_FOREST, if (!isNight) THEME_INDIGO else null, if (!isNight) THEME_PINK else null,
|
||||
THEME_OCEAN, if (!isNight) THEME_SAND else null, THEME_VIOLETTE
|
||||
) + prefs.all.keys.mapNotNull {
|
||||
when {
|
||||
it.startsWith(Settings.PREF_USER_COLORS_PREFIX) -> it.substringAfter(Settings.PREF_USER_COLORS_PREFIX)
|
||||
it.startsWith(Settings.PREF_USER_ALL_COLORS_PREFIX) -> it.substringAfter(Settings.PREF_USER_ALL_COLORS_PREFIX)
|
||||
else -> null
|
||||
}
|
||||
}.toSortedSet() // we don't want duplicates, and we want a consistent order
|
||||
fun getAvailableDefaultColors(prefs: SharedPreferences, isNight: Boolean) = listOfNotNull(
|
||||
if (!isNight) THEME_LIGHT else null, THEME_DARK,
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) THEME_DYNAMIC else null,
|
||||
if (prefs.getString(Settings.PREF_THEME_STYLE, Defaults.PREF_THEME_STYLE) == STYLE_HOLO) THEME_HOLO_WHITE else null,
|
||||
THEME_DARKER,
|
||||
THEME_BLACK,
|
||||
if (!isNight) THEME_BLUE_GRAY else null,
|
||||
if (!isNight) THEME_BROWN else null,
|
||||
THEME_CHOCOLATE,
|
||||
THEME_CLOUDY,
|
||||
THEME_FOREST,
|
||||
if (!isNight) THEME_INDIGO else null,
|
||||
if (!isNight) THEME_PINK else null,
|
||||
THEME_OCEAN,
|
||||
if (!isNight) THEME_SAND else null,
|
||||
THEME_VIOLETTE
|
||||
)
|
||||
val STYLES = arrayOf(STYLE_MATERIAL, STYLE_HOLO, STYLE_ROUNDED)
|
||||
|
||||
// These should be aligned with Keyboard.themeId and Keyboard.Case.keyboardTheme
|
||||
|
@ -326,13 +331,13 @@ private constructor(val themeId: Int, @JvmField val mStyleId: Int) {
|
|||
}
|
||||
}
|
||||
|
||||
fun writeUserColors(prefs: SharedPreferences, themeName: String, colors: Map<String, Pair<Int?, Boolean>>) {
|
||||
fun writeUserColors(prefs: SharedPreferences, themeName: String, colors: List<ColorSetting>) {
|
||||
val key = Settings.PREF_USER_COLORS_PREFIX + themeName
|
||||
val value = Json.encodeToString(colors.filterValues { it.first != null })
|
||||
val value = Json.encodeToString(colors.filter { it.color != null || it.auto == false })
|
||||
prefs.edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
fun readUserColors(prefs: SharedPreferences, themeName: String): Map<String, Pair<Int, Boolean>> {
|
||||
fun readUserColors(prefs: SharedPreferences, themeName: String): List<ColorSetting> {
|
||||
val key = Settings.PREF_USER_COLORS_PREFIX + themeName
|
||||
return Json.decodeFromString(prefs.getString(key, Defaults.PREF_USER_COLORS)!!)
|
||||
}
|
||||
|
@ -368,14 +373,16 @@ private constructor(val themeId: Int, @JvmField val mStyleId: Int) {
|
|||
return colorMap
|
||||
}
|
||||
|
||||
fun determineUserColor(colors: Map<String, Pair<Int?, Boolean>>, context: Context, colorName: String, isNight: Boolean): Int {
|
||||
val (color, auto) = colors[colorName] ?: (null to true)
|
||||
fun determineUserColor(colors: List<ColorSetting>, context: Context, colorName: String, isNight: Boolean): Int {
|
||||
val c = colors.firstOrNull { it.name == colorName }
|
||||
val color = c?.color
|
||||
val auto = c?.auto ?: true
|
||||
return if (auto || color == null)
|
||||
determineAutoColor(colors, colorName, isNight, context)
|
||||
else color
|
||||
}
|
||||
|
||||
private fun determineAutoColor(colors: Map<String, Pair<Int?, Boolean>>, colorName: String, isNight: Boolean, context: Context): Int {
|
||||
private fun determineAutoColor(colors: List<ColorSetting>, colorName: String, isNight: Boolean, context: Context): Int {
|
||||
when (colorName) {
|
||||
COLOR_ACCENT -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
|
@ -427,3 +434,8 @@ private constructor(val themeId: Int, @JvmField val mStyleId: Int) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ColorSetting(val name: String, val auto: Boolean?, val color: Int?) {
|
||||
var displayName = name
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package helium314.keyboard.latin
|
|||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import helium314.keyboard.keyboard.ColorSetting
|
||||
import helium314.keyboard.keyboard.KeyboardTheme
|
||||
import helium314.keyboard.latin.common.ColorType
|
||||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||
|
@ -167,14 +168,14 @@ fun checkVersionUpgrade(context: Context) {
|
|||
}
|
||||
// day colors
|
||||
val themeNameDay = context.getString(R.string.theme_name_user)
|
||||
val colorsDay = colorPrefsAndResIds.associate {
|
||||
val colorsDay = colorPrefsAndResIds.map {
|
||||
val pref = "theme_color_" + it.first
|
||||
val color = if (prefs.contains(pref)) prefs.getInt(pref, 0) else null
|
||||
val result = it.first to (color to prefs.getBoolean(pref + "_auto", true))
|
||||
val result = ColorSetting(it.first, prefs.getBoolean(pref + "_auto", true), color)
|
||||
prefs.edit().remove(pref).remove(pref + "_auto").apply()
|
||||
result
|
||||
}
|
||||
if (colorsDay.any { it.value.first != null }) {
|
||||
if (colorsDay.any { it.color != null }) {
|
||||
KeyboardTheme.writeUserColors(prefs, themeNameDay, colorsDay)
|
||||
}
|
||||
val moreColorsDay = prefs.getInt("theme_color_show_more_colors", 0)
|
||||
|
@ -190,14 +191,14 @@ fun checkVersionUpgrade(context: Context) {
|
|||
|
||||
// same for night colors
|
||||
val themeNameNight = context.getString(R.string.theme_name_user_night)
|
||||
val colorsNight = colorPrefsAndResIds.associate {
|
||||
val colorsNight = colorPrefsAndResIds.map {
|
||||
val pref = "theme_dark_color_" + it.first
|
||||
val color = if (prefs.contains(pref)) prefs.getInt(pref, 0) else null
|
||||
val result = it.first to (color to prefs.getBoolean(pref + "_auto", true))
|
||||
val result = ColorSetting(it.first, prefs.getBoolean(pref + "_auto", true), color)
|
||||
prefs.edit().remove(pref).remove(pref + "_auto").apply()
|
||||
result
|
||||
}
|
||||
if (colorsNight.any { it.value.first != null }) {
|
||||
if (colorsNight.any { it.color!= null }) {
|
||||
KeyboardTheme.writeUserColors(prefs, themeNameNight, colorsNight)
|
||||
}
|
||||
val moreColorsNight = prefs.getInt("theme_dark_color_show_more_colors", 0)
|
||||
|
|
|
@ -143,7 +143,7 @@ object Defaults {
|
|||
const val PREF_SHOW_SUGGESTION_INFOS = false
|
||||
const val PREF_FORCE_NON_DISTINCT_MULTITOUCH = false
|
||||
const val PREF_SLIDING_KEY_INPUT_PREVIEW = true
|
||||
const val PREF_USER_COLORS = "{}"
|
||||
const val PREF_USER_COLORS = "[]"
|
||||
const val PREF_USER_MORE_COLORS = 0
|
||||
const val PREF_USER_ALL_COLORS = ""
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.navigation.compose.rememberNavController
|
|||
import helium314.keyboard.settings.screens.AboutScreen
|
||||
import helium314.keyboard.settings.screens.AdvancedSettingsScreen
|
||||
import helium314.keyboard.settings.screens.AppearanceScreen
|
||||
import helium314.keyboard.settings.screens.ColorsScreen
|
||||
import helium314.keyboard.settings.screens.DebugScreen
|
||||
import helium314.keyboard.settings.screens.GestureTypingScreen
|
||||
import helium314.keyboard.settings.screens.MainSettingsScreen
|
||||
|
@ -95,16 +96,10 @@ fun SettingsNavHost(
|
|||
// )
|
||||
}
|
||||
composable(SettingsDestination.Colors) {
|
||||
// ColorsScreen(
|
||||
// night = false,
|
||||
// onClickBack = ::goBack
|
||||
// )
|
||||
ColorsScreen(isNight = false, onClickBack = ::goBack)
|
||||
}
|
||||
composable(SettingsDestination.ColorsNight) {
|
||||
// ColorsScreen(
|
||||
// night = true,
|
||||
// onClickBack = ::goBack
|
||||
// )
|
||||
ColorsScreen(isNight = true, onClickBack = ::goBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ package helium314.keyboard.settings.dialogs
|
|||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
|
@ -17,21 +17,19 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.PaintingStyle
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.skydoves.colorpicker.compose.AlphaSlider
|
||||
import com.github.skydoves.colorpicker.compose.BrightnessSlider
|
||||
import com.github.skydoves.colorpicker.compose.ColorEnvelope
|
||||
import com.github.skydoves.colorpicker.compose.HsvColorPicker
|
||||
import com.github.skydoves.colorpicker.compose.rememberColorPickerController
|
||||
|
||||
// todo:
|
||||
// setting from text doesn't work
|
||||
// weird effect on start, did this start with the top row showing colors?
|
||||
// text field doesn't look nice
|
||||
// for initial color picks performance is not good
|
||||
@Composable
|
||||
fun ColorPickerDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
|
@ -40,8 +38,17 @@ fun ColorPickerDialog(
|
|||
onConfirmed: (Int) -> Unit,
|
||||
) {
|
||||
val controller = rememberColorPickerController()
|
||||
val wheelPaint = Paint().apply {
|
||||
alpha = 0.5f
|
||||
style = PaintingStyle.Stroke
|
||||
strokeWidth = 5f
|
||||
color = Color.White
|
||||
}
|
||||
controller.wheelPaint = wheelPaint
|
||||
val barHeight = 35.dp
|
||||
var value by remember { mutableStateOf(TextFieldValue(initialColor.toString(16))) }
|
||||
val initialString = initialColor.toUInt().toString(16)
|
||||
var textValue by remember { mutableStateOf(TextFieldValue(initialString, TextRange(initialString.length))) }
|
||||
var currentColor by remember { mutableStateOf(Color(initialColor)) }
|
||||
ThreeButtonAlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onConfirmed = { onConfirmed(controller.selectedColor.value.toArgb()) },
|
||||
|
@ -56,7 +63,7 @@ fun ColorPickerDialog(
|
|||
.height(barHeight))
|
||||
{ }
|
||||
Surface(
|
||||
color = controller.selectedColor.value,
|
||||
color = currentColor,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.padding(end = 10.dp)
|
||||
.height(barHeight))
|
||||
|
@ -65,13 +72,15 @@ fun ColorPickerDialog(
|
|||
HsvColorPicker(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(0.6f)
|
||||
.height(300.dp)
|
||||
.padding(10.dp),
|
||||
controller = controller,
|
||||
onColorChanged = { colorEnvelope: ColorEnvelope ->
|
||||
value = TextFieldValue(colorEnvelope.hexCode)
|
||||
onColorChanged = {
|
||||
if (it.fromUser)
|
||||
textValue = TextFieldValue(it.hexCode, selection = TextRange(it.hexCode.length))
|
||||
currentColor = it.color
|
||||
},
|
||||
initialColor = Color(initialColor)
|
||||
initialColor = Color(initialColor),
|
||||
)
|
||||
AlphaSlider(
|
||||
modifier = Modifier
|
||||
|
@ -79,6 +88,8 @@ fun ColorPickerDialog(
|
|||
.padding(10.dp)
|
||||
.height(barHeight),
|
||||
controller = controller,
|
||||
initialColor = Color(initialColor),
|
||||
wheelPaint = wheelPaint
|
||||
)
|
||||
BrightnessSlider(
|
||||
modifier = Modifier
|
||||
|
@ -86,13 +97,18 @@ fun ColorPickerDialog(
|
|||
.padding(10.dp)
|
||||
.height(barHeight),
|
||||
controller = controller,
|
||||
initialColor = Color(initialColor),
|
||||
wheelPaint = wheelPaint
|
||||
)
|
||||
TextField(
|
||||
value = value,
|
||||
value = textValue,
|
||||
// KeyboardType.Password is a crappy way of avoiding suggestions... is there really no way in compose?
|
||||
keyboardOptions = KeyboardOptions(autoCorrectEnabled = false, keyboardType = KeyboardType.Password),
|
||||
onValueChange = {
|
||||
val androidColor = kotlin.runCatching { android.graphics.Color.parseColor("#$it") }.getOrNull()
|
||||
textValue = it
|
||||
val androidColor = kotlin.runCatching { android.graphics.Color.parseColor("#${it.text}") }.getOrNull()
|
||||
if (androidColor != null)
|
||||
controller.selectByColor(Color(androidColor), true)
|
||||
controller.selectByColor(Color(androidColor), false)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -103,5 +119,5 @@ fun ColorPickerDialog(
|
|||
@Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
ColorPickerDialog({}, android.graphics.Color.MAGENTA, "color name", {})
|
||||
ColorPickerDialog({}, -0x0f4488aa, "color name", {})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
package helium314.keyboard.settings.dialogs
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import helium314.keyboard.keyboard.KeyboardTheme
|
||||
import helium314.keyboard.latin.R
|
||||
import helium314.keyboard.latin.settings.Settings
|
||||
import helium314.keyboard.latin.utils.getStringResourceOrName
|
||||
import helium314.keyboard.latin.utils.prefs
|
||||
import helium314.keyboard.settings.Setting
|
||||
import helium314.keyboard.settings.SettingsDestination
|
||||
import helium314.keyboard.settings.keyboardNeedsReload
|
||||
|
||||
// specialized variant of ListPickerDialog
|
||||
@Composable
|
||||
fun ColorThemePickerDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
setting: Setting,
|
||||
isNight: Boolean,
|
||||
default: String
|
||||
) {
|
||||
val ctx = LocalContext.current
|
||||
val prefs = ctx.prefs()
|
||||
val defaultColors = KeyboardTheme.getAvailableDefaultColors(prefs, false)
|
||||
|
||||
// prefs.all is null in preview only
|
||||
val userColors = (prefs.all ?: mapOf(Settings.PREF_USER_COLORS_PREFIX + "usercolor" to "") ).keys.mapNotNull {
|
||||
when {
|
||||
it.startsWith(Settings.PREF_USER_COLORS_PREFIX) -> it.substringAfter(Settings.PREF_USER_COLORS_PREFIX)
|
||||
it.startsWith(Settings.PREF_USER_ALL_COLORS_PREFIX) -> it.substringAfter(Settings.PREF_USER_ALL_COLORS_PREFIX)
|
||||
else -> null
|
||||
}
|
||||
}.toSortedSet() // we don't want duplicates, and we want a consistent order
|
||||
val selectedColor = prefs.getString(setting.key, default)!!
|
||||
if (selectedColor !in defaultColors)
|
||||
userColors.add(selectedColor) // there are cases where we have no settings for a user theme
|
||||
|
||||
val colors = defaultColors + userColors + ""
|
||||
val state = rememberLazyListState()
|
||||
LaunchedEffect(selectedColor) {
|
||||
val index = colors.indexOf(selectedColor)
|
||||
if (index != -1) state.scrollToItem(index, -state.layoutInfo.viewportSize.height / 3)
|
||||
}
|
||||
ThreeButtonAlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
cancelButtonText = stringResource(R.string.dialog_close),
|
||||
onConfirmed = { },
|
||||
confirmButtonText = null,
|
||||
// neutralButtonText = stringResource(R.string.load),
|
||||
onNeutral = {
|
||||
// todo: launcher to select file
|
||||
// when importing make sure name is not in use
|
||||
},
|
||||
title = { Text(setting.title) },
|
||||
text = {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides MaterialTheme.typography.bodyLarge
|
||||
) {
|
||||
LazyColumn(state = state) {
|
||||
items(colors) { item ->
|
||||
if (item == "") {
|
||||
var textValue by remember { mutableStateOf(TextFieldValue()) }
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(painterResource(R.drawable.ic_plus), stringResource(R.string.add)) // todo: should it be a button?
|
||||
TextField(
|
||||
value = textValue,
|
||||
onValueChange = { textValue = it },
|
||||
modifier = Modifier.weight(1f),
|
||||
singleLine = true
|
||||
)
|
||||
IconButton(
|
||||
enabled = textValue.text.isNotEmpty() && textValue.text !in userColors,
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
prefs.edit().putString(setting.key, textValue.text).apply()
|
||||
if (isNight) SettingsDestination.navigateTo(SettingsDestination.ColorsNight)
|
||||
else SettingsDestination.navigateTo(SettingsDestination.Colors)
|
||||
keyboardNeedsReload = true
|
||||
}
|
||||
) { Icon(painterResource(R.drawable.ic_edit), null) }
|
||||
}
|
||||
} else {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
onDismissRequest()
|
||||
prefs.edit().putString(setting.key, item).apply()
|
||||
keyboardNeedsReload = true
|
||||
}
|
||||
.padding(start = 6.dp)
|
||||
.heightIn(min = 40.dp)
|
||||
) {
|
||||
RadioButton(
|
||||
selected = selectedColor == item,
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
prefs.edit().putString(setting.key, item).apply()
|
||||
keyboardNeedsReload = true
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = item.getStringResourceOrName("theme_name_", ctx),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (item in userColors) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
IconButton(
|
||||
onClick = { showDialog = true }
|
||||
) { Icon(painterResource(R.drawable.ic_bin), null) }
|
||||
IconButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
prefs.edit().putString(setting.key, item).apply()
|
||||
if (isNight) SettingsDestination.navigateTo(SettingsDestination.ColorsNight)
|
||||
else SettingsDestination.navigateTo(SettingsDestination.Colors)
|
||||
keyboardNeedsReload = true
|
||||
}
|
||||
) { Icon(painterResource(R.drawable.ic_edit), null) }
|
||||
if (showDialog)
|
||||
ConfirmationDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
text = { Text(stringResource(R.string.delete_confirmation, item)) },
|
||||
onConfirmed = {
|
||||
onDismissRequest()
|
||||
prefs.edit().remove(Settings.PREF_USER_COLORS_PREFIX + item)
|
||||
.remove(Settings.PREF_USER_ALL_COLORS_PREFIX + item)
|
||||
.remove(Settings.PREF_USER_MORE_COLORS_PREFIX + item).apply()
|
||||
if (item == selectedColor)
|
||||
prefs.edit().remove(setting.key).apply()
|
||||
keyboardNeedsReload = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewListPickerDialog() {
|
||||
ColorThemePickerDialog(
|
||||
onDismissRequest = {},
|
||||
setting = Setting(LocalContext.current, "", R.string.settings) {},
|
||||
default = "dark",
|
||||
isNight = true
|
||||
)
|
||||
}
|
|
@ -86,7 +86,6 @@ fun <T: Any> ListPickerDialog(
|
|||
)
|
||||
Text(
|
||||
text = getItemName(item),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,15 +17,12 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import helium314.keyboard.keyboard.KeyboardSwitcher
|
||||
import helium314.keyboard.keyboard.KeyboardTheme
|
||||
import helium314.keyboard.latin.R
|
||||
import helium314.keyboard.latin.settings.ColorsNightSettingsFragment
|
||||
import helium314.keyboard.latin.settings.ColorsSettingsFragment
|
||||
import helium314.keyboard.latin.settings.Defaults
|
||||
import helium314.keyboard.latin.settings.Settings
|
||||
import helium314.keyboard.latin.utils.Log
|
||||
import helium314.keyboard.latin.utils.getActivity
|
||||
import helium314.keyboard.latin.utils.getStringResourceOrName
|
||||
import helium314.keyboard.latin.utils.prefs
|
||||
import helium314.keyboard.latin.utils.switchTo
|
||||
import helium314.keyboard.settings.SettingsContainer
|
||||
import helium314.keyboard.settings.preferences.ListPreference
|
||||
import helium314.keyboard.settings.SettingsWithoutKey
|
||||
|
@ -36,6 +33,7 @@ import helium314.keyboard.settings.SettingsActivity
|
|||
import helium314.keyboard.settings.preferences.SliderPreference
|
||||
import helium314.keyboard.settings.preferences.SwitchPreference
|
||||
import helium314.keyboard.settings.Theme
|
||||
import helium314.keyboard.settings.dialogs.ColorThemePickerDialog
|
||||
import helium314.keyboard.settings.dialogs.CustomizeIconsDialog
|
||||
import helium314.keyboard.settings.dialogs.TextInputDialog
|
||||
import helium314.keyboard.settings.keyboardNeedsReload
|
||||
|
@ -58,14 +56,10 @@ fun AppearanceScreen(
|
|||
Settings.PREF_ICON_STYLE,
|
||||
Settings.PREF_CUSTOM_ICON_NAMES,
|
||||
Settings.PREF_THEME_COLORS,
|
||||
// if (prefs.getString(Settings.PREF_THEME_COLORS, Defaults.PREF_THEME_COLORS) == KeyboardTheme.THEME_USER)
|
||||
// SettingsWithoutKey.ADJUST_COLORS else null, // todo: remove, this should be part of the color selection menu
|
||||
Settings.PREF_THEME_KEY_BORDERS,
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
Settings.PREF_THEME_DAY_NIGHT else null,
|
||||
if (dayNightMode) Settings.PREF_THEME_COLORS_NIGHT else null,
|
||||
// if (dayNightMode && prefs.getString(Settings.PREF_THEME_COLORS_NIGHT, Defaults.PREF_THEME_COLORS_NIGHT) == KeyboardTheme.THEME_USER_NIGHT)
|
||||
// SettingsWithoutKey.ADJUST_COLORS_NIGHT else null, // todo: remove, this should be part of the color selection menu
|
||||
Settings.PREF_NAVBAR_COLOR,
|
||||
SettingsWithoutKey.BACKGROUND_IMAGE,
|
||||
SettingsWithoutKey.BACKGROUND_IMAGE_LANDSCAPE,
|
||||
|
@ -139,54 +133,44 @@ fun createAppearanceSettings(context: Context) = listOf(
|
|||
},
|
||||
Setting(context, Settings.PREF_THEME_COLORS, R.string.theme_colors) { setting ->
|
||||
val ctx = LocalContext.current
|
||||
val prefs = ctx.prefs()
|
||||
val b = (ctx.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState()
|
||||
if ((b?.value ?: 0) < 0)
|
||||
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
||||
val items = KeyboardTheme.getAvailableColors(ctx.prefs(), false).map {
|
||||
it.getStringResourceOrName("theme_name_", ctx) to it
|
||||
}
|
||||
ListPreference(
|
||||
setting,
|
||||
items,
|
||||
Defaults.PREF_THEME_COLORS
|
||||
) { keyboardNeedsReload = true }
|
||||
var showDialog by rememberSaveable { mutableStateOf(false) }
|
||||
Preference(
|
||||
name = setting.title,
|
||||
description = prefs.getString(setting.key, Defaults.PREF_THEME_COLORS)!!.getStringResourceOrName("theme_name_", ctx),
|
||||
onClick = { showDialog = true }
|
||||
)
|
||||
if (showDialog)
|
||||
ColorThemePickerDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
setting = setting,
|
||||
isNight = false,
|
||||
default = Defaults.PREF_THEME_COLORS
|
||||
)
|
||||
},
|
||||
Setting(context, Settings.PREF_THEME_COLORS_NIGHT, R.string.theme_colors_night) { setting ->
|
||||
val ctx = LocalContext.current
|
||||
val b = (ctx.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState()
|
||||
val prefs = ctx.prefs()
|
||||
if ((b?.value ?: 0) < 0)
|
||||
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
||||
val items = KeyboardTheme.getAvailableColors(ctx.prefs(), true).map {
|
||||
it.getStringResourceOrName("theme_name_", ctx) to it
|
||||
}
|
||||
ListPreference(
|
||||
setting,
|
||||
items,
|
||||
Defaults.PREF_THEME_COLORS_NIGHT
|
||||
) { keyboardNeedsReload = true }
|
||||
},
|
||||
/* Setting(context, SettingsWithoutKey.ADJUST_COLORS, R.string.select_user_colors, R.string.select_user_colors_summary) {
|
||||
val ctx = LocalContext.current
|
||||
var showDialog by rememberSaveable { mutableStateOf(false) }
|
||||
Preference(
|
||||
name = it.title,
|
||||
description = it.description,
|
||||
onClick = {
|
||||
ctx.getActivity()?.switchTo(ColorsSettingsFragment())
|
||||
//SettingsDestination.navigateTo(SettingsDestination.Colors) todo: soon
|
||||
}
|
||||
name = setting.title,
|
||||
description = prefs.getString(setting.key, Defaults.PREF_THEME_COLORS_NIGHT)!!.getStringResourceOrName("theme_name_", ctx),
|
||||
onClick = { showDialog = true }
|
||||
)
|
||||
if (showDialog)
|
||||
ColorThemePickerDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
setting = setting,
|
||||
isNight = true,
|
||||
default = Defaults.PREF_THEME_COLORS_NIGHT
|
||||
)
|
||||
},
|
||||
Setting(context, SettingsWithoutKey.ADJUST_COLORS_NIGHT, R.string.select_user_colors_night, R.string.select_user_colors_summary) {
|
||||
val ctx = LocalContext.current
|
||||
Preference(
|
||||
name = it.title,
|
||||
description = it.description,
|
||||
onClick = {
|
||||
ctx.getActivity()?.switchTo(ColorsNightSettingsFragment())
|
||||
//SettingsDestination.navigateTo(SettingsDestination.ColorsNight) todo: soon
|
||||
}
|
||||
)
|
||||
},*/
|
||||
Setting(context, Settings.PREF_THEME_KEY_BORDERS, R.string.key_borders) {
|
||||
SwitchPreference(it, Defaults.PREF_THEME_KEY_BORDERS)
|
||||
},
|
||||
|
|
|
@ -1,34 +1,161 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
package helium314.keyboard.settings.screens
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import helium314.keyboard.keyboard.ColorSetting
|
||||
import helium314.keyboard.keyboard.KeyboardTheme
|
||||
import helium314.keyboard.latin.R
|
||||
import helium314.keyboard.latin.common.ColorType
|
||||
import helium314.keyboard.latin.common.default
|
||||
import helium314.keyboard.latin.settings.Defaults
|
||||
import helium314.keyboard.latin.settings.Settings
|
||||
import helium314.keyboard.latin.settings.colorPrefsAndResIds
|
||||
import helium314.keyboard.latin.settings.getColorPrefsToHideInitially
|
||||
import helium314.keyboard.latin.utils.Log
|
||||
import helium314.keyboard.latin.utils.getActivity
|
||||
import helium314.keyboard.latin.utils.prefs
|
||||
import helium314.keyboard.settings.SearchScreen
|
||||
import helium314.keyboard.settings.SettingsActivity
|
||||
import helium314.keyboard.settings.Theme
|
||||
import helium314.keyboard.settings.dialogs.ColorPickerDialog
|
||||
import helium314.keyboard.settings.keyboardNeedsReload
|
||||
|
||||
@Composable
|
||||
fun ColorsScreen(
|
||||
night: Boolean,
|
||||
isNight: Boolean,
|
||||
onClickBack: () -> Unit
|
||||
) {
|
||||
// todo:
|
||||
// allow switching moreColors (three dot menu? dropdown / spinner for visibility?)
|
||||
// allow save (load should be in theme selector, maybe here too)
|
||||
// import/export should now also store theme name
|
||||
// handle name collisions on load by simply appending a number
|
||||
// allow editing theme name
|
||||
// make sure import of old colors works
|
||||
|
||||
var availableColors by remember { mutableStateOf(emptyList<ColorSetting>()) } // todo (later): type?
|
||||
// todo (later): save / load / type selection here? or in ... menu as previously?
|
||||
val ctx = LocalContext.current
|
||||
val prefs = ctx.prefs()
|
||||
val b = (ctx.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState()
|
||||
if ((b?.value ?: 0) < 0)
|
||||
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
||||
|
||||
val themeName = if (isNight) prefs.getString(Settings.PREF_THEME_COLORS_NIGHT, Defaults.PREF_THEME_COLORS_NIGHT)!!
|
||||
else prefs.getString(Settings.PREF_THEME_COLORS, Defaults.PREF_THEME_COLORS)!!
|
||||
val moreColors = KeyboardTheme.readUserMoreColors(prefs, themeName)
|
||||
val userColors = KeyboardTheme.readUserColors(prefs, themeName)
|
||||
val shownColors = if (moreColors == 2) {
|
||||
val allColors = KeyboardTheme.readUserAllColors(prefs, themeName)
|
||||
ColorType.entries.map {
|
||||
ColorSetting(it.name, null, allColors[it] ?: it.default())
|
||||
}
|
||||
} else {
|
||||
val toDisplay = colorPrefsAndResIds.map { (colorName, resId) ->
|
||||
val cs = userColors.firstOrNull { it.name == colorName } ?: ColorSetting(colorName, true, null)
|
||||
cs.displayName = stringResource(resId)
|
||||
cs
|
||||
}
|
||||
val colorsToHide = getColorPrefsToHideInitially(prefs)
|
||||
if (moreColors == 1) toDisplay
|
||||
else toDisplay.filter { it.color != null || it.name !in colorsToHide }
|
||||
}
|
||||
fun ColorSetting.displayColor() = if (auto == true) KeyboardTheme.determineUserColor(userColors, ctx, name, isNight)
|
||||
else color ?: KeyboardTheme.determineUserColor(userColors, ctx, name, isNight)
|
||||
|
||||
var chosenColor: ColorSetting? by remember { mutableStateOf(null) }
|
||||
SearchScreen(
|
||||
title = stringResource(if (night) R.string.select_user_colors_night else R.string.select_user_colors),
|
||||
title = themeName,
|
||||
onClickBack = onClickBack,
|
||||
filteredItems = { search -> availableColors.filter { it.displayName.contains(search, true) } },
|
||||
itemContent = { }
|
||||
filteredItems = { search -> shownColors.filter {
|
||||
it.displayName.split(" ", "_").any { it.startsWith(search, true) }
|
||||
} },
|
||||
itemContent = { colorSetting ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.clickable { chosenColor = colorSetting }
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.background(Color(colorSetting.displayColor()), shape = CircleShape)
|
||||
.size(50.dp)
|
||||
)
|
||||
Column(Modifier.weight(1f).padding(horizontal = 16.dp)) {
|
||||
Text(colorSetting.displayName)
|
||||
if (colorSetting.auto == true)
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides MaterialTheme.typography.bodyMedium,
|
||||
LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
|
||||
) {
|
||||
Text(stringResource(R.string.auto_user_color))
|
||||
}
|
||||
}
|
||||
if (colorSetting.auto != null)
|
||||
Switch(colorSetting.auto, onCheckedChange = {
|
||||
val oldUserColors = KeyboardTheme.readUserColors(prefs, themeName)
|
||||
val newUserColors = (oldUserColors + ColorSetting(colorSetting.name, it, colorSetting.color))
|
||||
.reversed().distinctBy { it.displayName }
|
||||
KeyboardTheme.writeUserColors(prefs, themeName, newUserColors)
|
||||
keyboardNeedsReload = true
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
if (chosenColor != null) {
|
||||
val color = chosenColor!!
|
||||
ColorPickerDialog(
|
||||
onDismissRequest = { chosenColor = null },
|
||||
initialColor = color.displayColor(),
|
||||
title = color.displayName,
|
||||
) {
|
||||
if (moreColors == 2) {
|
||||
val oldColors = KeyboardTheme.readUserAllColors(prefs, themeName)
|
||||
oldColors[ColorType.valueOf(color.name)] = it
|
||||
KeyboardTheme.writeUserAllColors(prefs, themeName, oldColors)
|
||||
} else {
|
||||
val oldUserColors = KeyboardTheme.readUserColors(prefs, themeName)
|
||||
val newUserColors = (oldUserColors + ColorSetting(color.name, false, it))
|
||||
.reversed().distinctBy { it.displayName }
|
||||
KeyboardTheme.writeUserColors(prefs, themeName, newUserColors)
|
||||
}
|
||||
keyboardNeedsReload = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ColorSetting(
|
||||
val key: String, // old, this should go away
|
||||
val displayName: String,
|
||||
var auto: Boolean, // not for all
|
||||
var color: Int
|
||||
)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
Theme(true) {
|
||||
Surface {
|
||||
ColorsScreen(false) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_user_dictionary_edit"
|
||||
android:src="@drawable/ic_edit"
|
||||
android:contentDescription="@null">
|
||||
</ImageView>
|
||||
|
||||
|
|
|
@ -987,4 +987,6 @@ New dictionary:
|
|||
<string name="customize_icons">Customize icons</string>
|
||||
<!-- Confirmation message when resetting all custom icons -->
|
||||
<string name="customize_icons_reset_message">Really reset all customized icons?</string>
|
||||
<!-- Confirmation message when deleting a something (used for custom colors) -->
|
||||
<string name="delete_confirmation">Really delete %s?</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Reference in a new issue