add customize icons dialog

This commit is contained in:
Helium314 2025-02-01 10:32:06 +01:00
parent e016d13410
commit 315ac3b049
7 changed files with 184 additions and 21 deletions

View file

@ -1,9 +1,15 @@
package helium314.keyboard.latin.utils
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.content.SharedPreferences
import android.view.View
import android.widget.RelativeLayout
import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.commit
import helium314.keyboard.latin.R
// generic extension functions
@ -67,4 +73,13 @@ fun Context.getActivity(): ComponentActivity? {
return componentActivity
}
// todo: should not be necessary after full pref switch to compose
fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) {
(this as AppCompatActivity).supportFragmentManager.commit {
findViewById<RelativeLayout>(R.id.settingsFragmentContainer).visibility = View.VISIBLE
replace(R.id.settingsFragmentContainer, fragment)
addToBackStack(null)
}
}
fun Context.prefs(): SharedPreferences = DeviceProtectedUtils.getSharedPreferences(this)

View file

@ -31,7 +31,6 @@ 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.style.Hyphens
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview

View file

@ -15,14 +15,18 @@ import kotlinx.coroutines.flow.MutableStateFlow
// todo (roughly in order)
// make all prefs actually work
// default buttons for toolbar key(s) customizer, icon customizer, and toolbar reorder dialog
// make a dialog wrapper that has a default button?
// try moving the recomposition of pref change somewhere else, so it's not duplicated everywhere
// make the pref lists more compact (compare with old settings)
// try making text size similar to old state (also in dialogs)
// check whether dialogs have the same colors, i think currently it's a bit inconsistent
// check dark and light theme (don't have dynamic)
// rename both settingsActivities
// work on todos in other files
// use better / more structured and clear names and arrangement of files
// the prefDef and AllPrefs, also be clear about pref <-> key <-> prefKey (all used, probably should be latter)
// there is a lot more ambiguous naming...
// animations when stuff (dis)appears
// LaunchedEffect, AnimatedVisibility
// performance
@ -40,7 +44,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
// merge main to implement all the new settings
// consider IME insets when searching
// dialogs should be rememberSaveable to survive display orientation change and stuff?
// default buttons for toolbar key(s) customizer and toolbar reorder dialog
// try making old fragment back stuff work better, and try the different themes (with and without top bar)
// any way to get rid of the "old" background on starting settings? probably comes from app theme, can we avoid it?
// consider using simple list picker dialog (but the "full" one is probably better for language settings stuff)

View file

@ -0,0 +1,142 @@
package helium314.keyboard.settings.dialogs
import android.graphics.drawable.VectorDrawable
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
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.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.util.TypedValueCompat
import helium314.keyboard.keyboard.internal.KeyboardIconsSet
import helium314.keyboard.latin.R
import helium314.keyboard.latin.customIconNames
import helium314.keyboard.latin.utils.getStringResourceOrName
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.screens.GetIcon
import kotlinx.serialization.json.Json
@Composable
fun CustomizeIconsDialog(
prefKey: String,
onDismissRequest: () -> Unit,
) {
val state = rememberLazyListState()
val ctx = LocalContext.current
val iconsAndNames = KeyboardIconsSet.getAllIcons(ctx).keys.map { iconName ->
val name = iconName.getStringResourceOrName("", ctx)
if (name == iconName) iconName to iconName.getStringResourceOrName("label_", ctx)
else iconName to name
}.sortedBy { it.second }
var showIconDialog: Pair<String, String>? by remember { mutableStateOf(null) }
val prefs = ctx.prefs()
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = { }, // no confirm button
dismissButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(R.string.dialog_close)) } },
title = { Text(stringResource(R.string.customize_icons)) },
text = {
LazyColumn(state = state) {
items(iconsAndNames, key = { it.first }) { (iconName, displayName) ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { showIconDialog = iconName to displayName }
) {
KeyboardIconsSet.instance.GetIcon(iconName)
Text(displayName, Modifier.weight(1f))
}
}
}
},
)
if (showIconDialog != null) {
val iconName = showIconDialog!!.first
val allIcons = KeyboardIconsSet.getAllIcons(ctx)
val iconsForName = allIcons[iconName].orEmpty()
val iconsSet = mutableSetOf<Int>()
iconsSet.addAll(iconsForName)
KeyboardIconsSet.getAllIcons(ctx).forEach { iconsSet.addAll(it.value) } // is this called again on UI interaction?
val icons = iconsSet.toList()
var selectedIcon by remember { mutableStateOf(KeyboardIconsSet.instance.iconIds[iconName]) }
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = { TextButton(
onClick = {
onDismissRequest()
runCatching {
val newIcons = customIconNames(prefs).toMutableMap()
newIcons[iconName] = selectedIcon?.let { ctx.resources.getResourceEntryName(it) } ?: return@runCatching
prefs.edit().putString(prefKey, Json.encodeToString(newIcons)).apply()
KeyboardIconsSet.instance.loadIcons(ctx)
}
// todo: show outer dialog again and reload icons?
}
) { Text(stringResource(android.R.string.ok)) } },
dismissButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(android.R.string.cancel)) } },
title = { Text(showIconDialog!!.second) },
text = {
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 64.dp)
) {
items(icons, key = { it }) { resId ->
val drawable = ContextCompat.getDrawable(ctx, resId)?.mutate() ?: return@items
val color = if (resId == selectedIcon) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.onSurface
CompositionLocalProvider(
LocalContentColor provides color
) {
Box(
Modifier.size(40.dp).clickable { selectedIcon = resId },
contentAlignment = Alignment.Center
) {
if (drawable is VectorDrawable)
Icon(painterResource(resId), null, Modifier.fillMaxSize(0.8f))
else {
val px = TypedValueCompat.dpToPx(40f, ctx.resources.displayMetrics).toInt()
Icon(drawable.toBitmap(px, px).asImageBitmap(), null, Modifier.fillMaxSize(0.8f))
}
}
}
}
}
},
)
}
}
@Preview
@Composable
private fun Preview() {
KeyboardIconsSet.instance.loadIcons(LocalContext.current)
CustomizeIconsDialog(
prefKey = "",
onDismissRequest = { },
)
}

View file

@ -15,12 +15,15 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
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.Settings
import helium314.keyboard.latin.settings.SettingsValues
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.AllPrefs
import helium314.keyboard.settings.ListPreference
import helium314.keyboard.settings.NonSettingsPrefs
@ -29,10 +32,10 @@ import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.PreferenceCategory
import helium314.keyboard.settings.SearchPrefScreen
import helium314.keyboard.settings.SettingsActivity2
import helium314.keyboard.settings.SettingsDestination
import helium314.keyboard.settings.SliderPreference
import helium314.keyboard.settings.SwitchPreference
import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.dialogs.CustomizeIconsDialog
@Composable
fun AppearanceScreen(
@ -43,7 +46,7 @@ fun AppearanceScreen(
val b = (LocalContext.current.getActivity() as? SettingsActivity2)?.prefChanged?.collectAsState()
if (b?.value ?: 0 < 0)
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
val dayNightMode = Settings.readDayNightPref(prefs, ctx.resources)
val dayNightMode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && Settings.readDayNightPref(prefs, ctx.resources)
val lightTheme = prefs.getString(Settings.PREF_THEME_COLORS, KeyboardTheme.THEME_LIGHT)
val darkTheme = prefs.getString(Settings.PREF_THEME_COLORS_NIGHT, KeyboardTheme.THEME_DARK)
SearchPrefScreen(
@ -58,6 +61,7 @@ fun AppearanceScreen(
if (lightTheme == KeyboardTheme.THEME_USER)
SettingsActivity2.allPrefs.map[NonSettingsPrefs.ADJUST_COLORS]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_KEY_BORDERS]!!.Preference()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_DAY_NIGHT]!!.Preference()
if (dayNightMode)
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_COLORS_NIGHT]!!.Preference()
@ -108,7 +112,10 @@ fun createAppearancePrefs(context: Context) = listOf(
Preference(
name = def.title,
onClick = { showDialog = true }
) // todo: create and show the dialog
)
if (showDialog) {
CustomizeIconsDialog(def.key) { showDialog = false }
}
},
PrefDef(context, Settings.PREF_THEME_COLORS, R.string.theme_colors) { def ->
val ctx = LocalContext.current
@ -139,17 +146,25 @@ fun createAppearancePrefs(context: Context) = listOf(
)
},
PrefDef(context, NonSettingsPrefs.ADJUST_COLORS, R.string.select_user_colors, R.string.select_user_colors_summary) { def ->
val ctx = LocalContext.current
Preference(
name = def.title,
description = def.description,
onClick = { SettingsDestination.navigateTo(SettingsDestination.Colors) }
onClick = {
ctx.getActivity()?.switchTo(ColorsSettingsFragment())
//SettingsDestination.navigateTo(SettingsDestination.Colors) todo: later
}
)
},
PrefDef(context, NonSettingsPrefs.ADJUST_COLORS_NIGHT, R.string.select_user_colors_night, R.string.select_user_colors_summary) { def ->
val ctx = LocalContext.current
Preference(
name = def.title,
description = def.description,
onClick = { SettingsDestination.navigateTo(SettingsDestination.ColorsNight) }
onClick = {
ctx.getActivity()?.switchTo(ColorsNightSettingsFragment())
//SettingsDestination.navigateTo(SettingsDestination.ColorsNight) todo: later
}
)
},
PrefDef(context, Settings.PREF_THEME_KEY_BORDERS, R.string.key_borders) { def ->

View file

@ -1,10 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.screens
import android.app.Activity
import android.view.View
import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
@ -14,7 +10,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.fragment.app.commit
import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.AboutFragment
import helium314.keyboard.latin.settings.AdvancedSettingsFragment
@ -26,6 +21,7 @@ import helium314.keyboard.latin.settings.PreferencesSettingsFragment
import helium314.keyboard.latin.settings.ToolbarSettingsFragment
import helium314.keyboard.latin.utils.JniUtils
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.switchTo
import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.PreferenceCategory
import helium314.keyboard.settings.SearchPrefScreen
@ -159,14 +155,6 @@ fun MainSettingsScreen(
}
}
fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) {
(this as AppCompatActivity).supportFragmentManager.commit {
findViewById<RelativeLayout>(R.id.settingsFragmentContainer).visibility = View.VISIBLE
replace(R.id.settingsFragmentContainer, fragment)
addToBackStack(null)
}
}
@Preview
@Composable
private fun PreviewScreen() {

View file

@ -26,6 +26,7 @@ import helium314.keyboard.latin.settings.UserDictionaryListFragment
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.latin.utils.switchTo
import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.ListPreference
import helium314.keyboard.settings.NonSettingsPrefs