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 package helium314.keyboard.latin.utils
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.SharedPreferences import android.content.SharedPreferences
import android.view.View
import android.widget.RelativeLayout
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.commit
import helium314.keyboard.latin.R
// generic extension functions // generic extension functions
@ -67,4 +73,13 @@ fun Context.getActivity(): ComponentActivity? {
return 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) 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.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource 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.Hyphens
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview

View file

@ -15,14 +15,18 @@ import kotlinx.coroutines.flow.MutableStateFlow
// todo (roughly in order) // todo (roughly in order)
// make all prefs actually work // 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 // 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) // make the pref lists more compact (compare with old settings)
// try making text size similar to old state (also in dialogs) // 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 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 // rename both settingsActivities
// work on todos in other files // work on todos in other files
// use better / more structured and clear names and arrangement of 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) // 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 // animations when stuff (dis)appears
// LaunchedEffect, AnimatedVisibility // LaunchedEffect, AnimatedVisibility
// performance // performance
@ -40,7 +44,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
// merge main to implement all the new settings // merge main to implement all the new settings
// consider IME insets when searching // consider IME insets when searching
// dialogs should be rememberSaveable to survive display orientation change and stuff? // 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) // 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? // 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) // 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 androidx.compose.ui.tooling.preview.Preview
import helium314.keyboard.keyboard.KeyboardTheme import helium314.keyboard.keyboard.KeyboardTheme
import helium314.keyboard.latin.R 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.Settings
import helium314.keyboard.latin.settings.SettingsValues import helium314.keyboard.latin.settings.SettingsValues
import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.getStringResourceOrName
import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.latin.utils.switchTo
import helium314.keyboard.settings.AllPrefs import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.ListPreference import helium314.keyboard.settings.ListPreference
import helium314.keyboard.settings.NonSettingsPrefs import helium314.keyboard.settings.NonSettingsPrefs
@ -29,10 +32,10 @@ import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.PreferenceCategory import helium314.keyboard.settings.PreferenceCategory
import helium314.keyboard.settings.SearchPrefScreen import helium314.keyboard.settings.SearchPrefScreen
import helium314.keyboard.settings.SettingsActivity2 import helium314.keyboard.settings.SettingsActivity2
import helium314.keyboard.settings.SettingsDestination
import helium314.keyboard.settings.SliderPreference import helium314.keyboard.settings.SliderPreference
import helium314.keyboard.settings.SwitchPreference import helium314.keyboard.settings.SwitchPreference
import helium314.keyboard.settings.Theme import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.dialogs.CustomizeIconsDialog
@Composable @Composable
fun AppearanceScreen( fun AppearanceScreen(
@ -43,7 +46,7 @@ fun AppearanceScreen(
val b = (LocalContext.current.getActivity() as? SettingsActivity2)?.prefChanged?.collectAsState() val b = (LocalContext.current.getActivity() as? SettingsActivity2)?.prefChanged?.collectAsState()
if (b?.value ?: 0 < 0) if (b?.value ?: 0 < 0)
Log.v("irrelevant", "stupid way to trigger recomposition on preference change") 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 lightTheme = prefs.getString(Settings.PREF_THEME_COLORS, KeyboardTheme.THEME_LIGHT)
val darkTheme = prefs.getString(Settings.PREF_THEME_COLORS_NIGHT, KeyboardTheme.THEME_DARK) val darkTheme = prefs.getString(Settings.PREF_THEME_COLORS_NIGHT, KeyboardTheme.THEME_DARK)
SearchPrefScreen( SearchPrefScreen(
@ -58,7 +61,8 @@ fun AppearanceScreen(
if (lightTheme == KeyboardTheme.THEME_USER) if (lightTheme == KeyboardTheme.THEME_USER)
SettingsActivity2.allPrefs.map[NonSettingsPrefs.ADJUST_COLORS]!!.Preference() SettingsActivity2.allPrefs.map[NonSettingsPrefs.ADJUST_COLORS]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_KEY_BORDERS]!!.Preference() SettingsActivity2.allPrefs.map[Settings.PREF_THEME_KEY_BORDERS]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_DAY_NIGHT]!!.Preference() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_DAY_NIGHT]!!.Preference()
if (dayNightMode) if (dayNightMode)
SettingsActivity2.allPrefs.map[Settings.PREF_THEME_COLORS_NIGHT]!!.Preference() SettingsActivity2.allPrefs.map[Settings.PREF_THEME_COLORS_NIGHT]!!.Preference()
if (dayNightMode && darkTheme == KeyboardTheme.THEME_USER_NIGHT) if (dayNightMode && darkTheme == KeyboardTheme.THEME_USER_NIGHT)
@ -108,7 +112,10 @@ fun createAppearancePrefs(context: Context) = listOf(
Preference( Preference(
name = def.title, name = def.title,
onClick = { showDialog = true } 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 -> PrefDef(context, Settings.PREF_THEME_COLORS, R.string.theme_colors) { def ->
val ctx = LocalContext.current 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 -> PrefDef(context, NonSettingsPrefs.ADJUST_COLORS, R.string.select_user_colors, R.string.select_user_colors_summary) { def ->
val ctx = LocalContext.current
Preference( Preference(
name = def.title, name = def.title,
description = def.description, 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 -> PrefDef(context, NonSettingsPrefs.ADJUST_COLORS_NIGHT, R.string.select_user_colors_night, R.string.select_user_colors_summary) { def ->
val ctx = LocalContext.current
Preference( Preference(
name = def.title, name = def.title,
description = def.description, 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 -> 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 // SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.screens 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.Icon
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable 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.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.fragment.app.commit
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.AboutFragment import helium314.keyboard.latin.settings.AboutFragment
import helium314.keyboard.latin.settings.AdvancedSettingsFragment 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.settings.ToolbarSettingsFragment
import helium314.keyboard.latin.utils.JniUtils import helium314.keyboard.latin.utils.JniUtils
import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.switchTo
import helium314.keyboard.settings.Preference import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.PreferenceCategory import helium314.keyboard.settings.PreferenceCategory
import helium314.keyboard.settings.SearchPrefScreen 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 @Preview
@Composable @Composable
private fun PreviewScreen() { 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.Log
import helium314.keyboard.latin.utils.getActivity import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.latin.utils.switchTo
import helium314.keyboard.settings.AllPrefs import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.ListPreference import helium314.keyboard.settings.ListPreference
import helium314.keyboard.settings.NonSettingsPrefs import helium314.keyboard.settings.NonSettingsPrefs