complete preferences screen

This commit is contained in:
Helium314 2025-02-06 15:52:54 +01:00
parent 8f5ef56f60
commit ae461fe8b6
4 changed files with 205 additions and 83 deletions

View file

@ -31,17 +31,22 @@ 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
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.edit import androidx.core.content.edit
import helium314.keyboard.keyboard.internal.KeyboardIconsSet
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
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.prefs import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.dialogs.ListPickerDialog import helium314.keyboard.settings.dialogs.ListPickerDialog
import helium314.keyboard.settings.dialogs.ReorderDialog
import helium314.keyboard.settings.dialogs.SliderDialog import helium314.keyboard.settings.dialogs.SliderDialog
import helium314.keyboard.settings.screens.GetIcon
// taken from StreetComplete (and a bit SCEE) // taken from StreetComplete (and a bit SCEE)
@ -256,6 +261,91 @@ fun <T: Any> ListPreference(
} }
} }
@Composable
fun ReorderSwitchPreference(def: PrefDef, default: String) {
var showDialog by remember { mutableStateOf(false) }
Preference(
name = def.title,
description = def.description,
onClick = { showDialog = true },
)
if (showDialog) {
val ctx = LocalContext.current
val prefs = ctx.prefs()
val items = prefs.getString(def.key, default)!!.split(";").mapTo(ArrayList()) {
val both = it.split(",")
KeyAndState(both.first(), both.last().toBoolean())
}
ReorderDialog(
onConfirmed = { reorderedItems ->
val value = reorderedItems.joinToString(";") { it.name + "," + it.state }
prefs.edit().putString(def.key, value).apply()
keyboardNeedsReload = true
},
onDismissRequest = { showDialog = false },
onNeutral = { prefs.edit().remove(def.key).apply() },
neutralButtonText = if (prefs.contains(def.key)) stringResource(R.string.button_default) else null,
items = items,
title = { Text(def.title) },
displayItem = { item ->
var checked by remember { mutableStateOf(item.state) }
Row(verticalAlignment = Alignment.CenterVertically) {
KeyboardIconsSet.instance.GetIcon(item.name)
val text = item.name.lowercase().getStringResourceOrName("", ctx)
Text(text, Modifier.weight(1f))
Switch(
checked = checked,
onCheckedChange = { item.state = it; checked = it }
)
}
},
getKey = { it.name }
)
}
}
@Composable
fun ToolbarKeyReorderDialog(
prefKey: String,
default: String,
title: String,
onDismiss: () -> Unit
) {
val ctx = LocalContext.current
val prefs = ctx.prefs()
val items = prefs.getString(prefKey, default)!!.split(";").mapTo(ArrayList()) {
val both = it.split(",")
KeyAndState(both.first(), both.last().toBoolean())
}
ReorderDialog(
onConfirmed = { reorderedItems ->
val value = reorderedItems.joinToString(";") { it.name + "," + it.state }
prefs.edit().putString(prefKey, value).apply()
keyboardNeedsReload = true
},
onDismissRequest = onDismiss,
onNeutral = { prefs.edit().remove(prefKey).apply() },
neutralButtonText = if (prefs.contains(prefKey)) stringResource(R.string.button_default) else null,
items = items,
title = { Text(title) },
displayItem = { item ->
var checked by remember { mutableStateOf(item.state) }
Row(verticalAlignment = Alignment.CenterVertically) {
KeyboardIconsSet.instance.GetIcon(item.name)
val text = item.name.lowercase().getStringResourceOrName("", ctx)
Text(text, Modifier.weight(1f))
Switch(
checked = checked,
onCheckedChange = { item.state = it; checked = it }
)
}
},
getKey = { it.name }
)
}
private class KeyAndState(var name: String, var state: Boolean)
private fun <T: Any> getPrefOfType(prefs: SharedPreferences, key: String, default: T): T = private fun <T: Any> getPrefOfType(prefs: SharedPreferences, key: String, default: T): T =
when (default) { when (default) {
is String -> prefs.getString(key, default) is String -> prefs.getString(key, default)

View file

@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
// todo (roughly in order) // todo (roughly in order)
// make all prefs actually work // make all prefs actually work
// preferences
// advanced (not much) // advanced (not much)
// 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)
@ -23,12 +22,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
// more similar dialog style args (for all dialogs, or for none) // more similar dialog style args (for all dialogs, or for none)
// 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
// see all the properties for each alertDialog -> any way to set it in a single place? // see all the properties for each alertDialog -> any way to set it in a single place?
// yes/no/default can now be confirmDialog
// title too huge for bg image and text on spacebar dialogs, also maybe somewhere else -> where to set in one place? // title too huge for bg image and text on spacebar dialogs, also maybe somewhere else -> where to set in one place?
// check dark and light theme (don't have dynamic) // 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
// calling KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) while keyboard is showing shows just full screen background // calling KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) while keyboard is showing shows just full screen background
// but reload while keyboard is showing would be great (isn't it at least semi-done when changing one-handed mode?)
// 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... // there is a lot more ambiguous naming...
@ -64,6 +63,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
// color settings (should at least change how colors are stored, and have a color search/filter) // color settings (should at least change how colors are stored, and have a color search/filter)
// allow users to add custom themes instead of only having a single one (maybe also switchable in colors settings) // allow users to add custom themes instead of only having a single one (maybe also switchable in colors settings)
// one single place for default values (to be used in composables and settings) // one single place for default values (to be used in composables and settings)
// does it make sense to put this into PrefDef?
// make auto_correct_threshold a float directly with the list pref (needs pref upgrade) // make auto_correct_threshold a float directly with the list pref (needs pref upgrade)
// using context.prefs() outside settings // using context.prefs() outside settings
// merge PREF_TOOLBAR_CUSTOM_KEY_CODES and PREF_TOOLBAR_CUSTOM_LONGPRESS_CODES into one pref (don't forget settings upgrade) // merge PREF_TOOLBAR_CUSTOM_KEY_CODES and PREF_TOOLBAR_CUSTOM_LONGPRESS_CODES into one pref (don't forget settings upgrade)

View file

@ -4,18 +4,34 @@ import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
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.compose.ui.unit.dp
import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.latin.AudioAndHapticFeedbackManager import helium314.keyboard.latin.AudioAndHapticFeedbackManager
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.POPUP_KEYS_LABEL_DEFAULT
import helium314.keyboard.latin.utils.POPUP_KEYS_ORDER_DEFAULT
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.getEnabledSubtypes
import helium314.keyboard.latin.utils.locale
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.AllPrefs import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.ListPreference
import helium314.keyboard.settings.PrefDef import helium314.keyboard.settings.PrefDef
import helium314.keyboard.settings.PreferenceCategory
import helium314.keyboard.settings.ReorderSwitchPreference
import helium314.keyboard.settings.SearchPrefScreen import helium314.keyboard.settings.SearchPrefScreen
import helium314.keyboard.settings.SettingsActivity2 import helium314.keyboard.settings.SettingsActivity2
import helium314.keyboard.settings.SliderPreference import helium314.keyboard.settings.SliderPreference
import helium314.keyboard.settings.SwitchPreference
import helium314.keyboard.settings.Theme import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.keyboardNeedsReload
@Composable @Composable
fun PreferencesScreen( fun PreferencesScreen(
@ -25,13 +41,100 @@ fun PreferencesScreen(
onClickBack = onClickBack, onClickBack = onClickBack,
title = stringResource(R.string.settings_screen_preferences), title = stringResource(R.string.settings_screen_preferences),
) { ) {
SettingsActivity2.allPrefs.map[Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME]!!.Preference() val prefs = LocalContext.current.prefs()
SettingsActivity2.allPrefs.map[Settings.PREF_VIBRATION_DURATION_SETTINGS]!!.Preference() val b = (LocalContext.current.getActivity() as? SettingsActivity2)?.prefChanged?.collectAsState()
SettingsActivity2.allPrefs.map[Settings.PREF_KEYPRESS_SOUND_VOLUME]!!.Preference() if ((b?.value ?: 0) < 0)
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
PreferenceCategory(stringResource(R.string.settings_category_input)) {
SettingsActivity2.allPrefs.map[Settings.PREF_SHOW_HINTS]!!.Preference()
if (prefs.getBoolean(Settings.PREF_SHOW_HINTS, true))
SettingsActivity2.allPrefs.map[Settings.PREF_POPUP_KEYS_LABELS_ORDER]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_POPUP_KEYS_ORDER]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_SHOW_POPUP_HINTS]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_POPUP_ON]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_VIBRATE_ON]!!.Preference()
if (prefs.getBoolean(Settings.PREF_VIBRATE_ON, true))
SettingsActivity2.allPrefs.map[Settings.PREF_VIBRATION_DURATION_SETTINGS]!!.Preference()
if (prefs.getBoolean(Settings.PREF_VIBRATE_ON, true))
SettingsActivity2.allPrefs.map[Settings.PREF_VIBRATE_IN_DND_MODE]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_SOUND_ON]!!.Preference()
if (prefs.getBoolean(Settings.PREF_SOUND_ON, true))
SettingsActivity2.allPrefs.map[Settings.PREF_KEYPRESS_SOUND_VOLUME]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME]!!.Preference()
}
PreferenceCategory(stringResource(R.string.settings_category_additional_keys)) {
SettingsActivity2.allPrefs.map[Settings.PREF_SHOW_NUMBER_ROW]!!.Preference()
if (getEnabledSubtypes(prefs, true).any { it.locale().language in localesWithLocalizedNumberRow })
SettingsActivity2.allPrefs.map[Settings.PREF_LOCALIZED_NUMBER_ROW]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_LANGUAGE_SWITCH_KEY]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_SHOW_EMOJI_KEY]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_REMOVE_REDUNDANT_POPUPS]!!.Preference()
}
PreferenceCategory(stringResource(R.string.settings_category_clipboard_history)) {
SettingsActivity2.allPrefs.map[Settings.PREF_ENABLE_CLIPBOARD_HISTORY]!!.Preference()
if (prefs.getBoolean(Settings.PREF_ENABLE_CLIPBOARD_HISTORY, true))
SettingsActivity2.allPrefs.map[Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME]!!.Preference()
}
} }
} }
fun createPreferencesPrefs(context: Context) = listOf( fun createPreferencesPrefs(context: Context) = listOf(
PrefDef(context, Settings.PREF_SHOW_HINTS, R.string.show_hints, R.string.show_hints_summary) {
SwitchPreference(it, true)
},
PrefDef(context, Settings.PREF_POPUP_KEYS_LABELS_ORDER, R.string.hint_source) {
ReorderSwitchPreference(it, POPUP_KEYS_LABEL_DEFAULT)
},
PrefDef(context, Settings.PREF_POPUP_KEYS_ORDER, R.string.popup_order) {
ReorderSwitchPreference(it, POPUP_KEYS_ORDER_DEFAULT)
},
PrefDef(context, Settings.PREF_SHOW_POPUP_HINTS, R.string.show_popup_hints, R.string.show_popup_hints_summary) {
SwitchPreference(it, false) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_POPUP_ON, R.string.popup_on_keypress) {
val dm = LocalContext.current.resources.displayMetrics
val px600 = with(LocalDensity.current) { 600.dp.toPx() }
SwitchPreference(it, dm.widthPixels >= px600 || dm.heightPixels >= px600)
},
PrefDef(context, Settings.PREF_VIBRATE_ON, R.string.vibrate_on_keypress) {
SwitchPreference(it, false)
},
PrefDef(context, Settings.PREF_VIBRATE_IN_DND_MODE, R.string.vibrate_in_dnd_mode) {
SwitchPreference(it, false)
},
PrefDef(context, Settings.PREF_SOUND_ON, R.string.sound_on_keypress) {
SwitchPreference(it, false)
},
PrefDef(context, Settings.PREF_ENABLE_CLIPBOARD_HISTORY, R.string.enable_clipboard_history, R.string.enable_clipboard_history_summary) {
SwitchPreference(it, true)
},
PrefDef(context, Settings.PREF_SHOW_NUMBER_ROW, R.string.number_row, R.string.number_row_summary) {
SwitchPreference(it, false) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_LOCALIZED_NUMBER_ROW, R.string.localized_number_row, R.string.localized_number_row_summary) {
SwitchPreference(it, true) { KeyboardLayoutSet.onSystemLocaleChanged() }
},
PrefDef(context, Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, R.string.show_language_switch_key) {
SwitchPreference(it, false) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_LANGUAGE_SWITCH_KEY, R.string.language_switch_key_behavior) {
ListPreference(
it,
listOf(
"internal" to stringResource(R.string.switch_language),
"input_method" to stringResource(R.string.language_switch_key_switch_input_method),
"both" to stringResource(R.string.language_switch_key_switch_both)
),
"internal"
) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_SHOW_EMOJI_KEY, R.string.show_emoji_key) {
SwitchPreference(it, false)
},
PrefDef(context, Settings.PREF_REMOVE_REDUNDANT_POPUPS, R.string.remove_redundant_popups, R.string.remove_redundant_popups_summary) {
SwitchPreference(it, false) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, R.string.clipboard_history_retention_time) { def -> PrefDef(context, Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, R.string.clipboard_history_retention_time) { def ->
SliderPreference( SliderPreference(
name = def.title, name = def.title,
@ -73,6 +176,9 @@ fun createPreferencesPrefs(context: Context) = listOf(
}, },
) )
// todo: not good to have it hardcoded, but reading a bunch of files may be noticeably slow
private val localesWithLocalizedNumberRow = listOf("ar", "bn", "fa", "gu", "hi", "kn", "mr", "ne", "ur")
@Preview @Preview
@Composable @Composable
private fun Preview() { private fun Preview() {

View file

@ -37,6 +37,7 @@ import helium314.keyboard.settings.AllPrefs
import helium314.keyboard.settings.NonSettingsPrefs import helium314.keyboard.settings.NonSettingsPrefs
import helium314.keyboard.settings.PrefDef import helium314.keyboard.settings.PrefDef
import helium314.keyboard.settings.Preference import helium314.keyboard.settings.Preference
import helium314.keyboard.settings.ReorderSwitchPreference
import helium314.keyboard.settings.SearchPrefScreen import helium314.keyboard.settings.SearchPrefScreen
import helium314.keyboard.settings.SettingsActivity2 import helium314.keyboard.settings.SettingsActivity2
import helium314.keyboard.settings.SwitchPreference import helium314.keyboard.settings.SwitchPreference
@ -66,46 +67,13 @@ fun ToolbarScreen(
fun createToolbarPrefs(context: Context) = listOf( fun createToolbarPrefs(context: Context) = listOf(
PrefDef(context, Settings.PREF_TOOLBAR_KEYS, R.string.toolbar_keys) { def -> PrefDef(context, Settings.PREF_TOOLBAR_KEYS, R.string.toolbar_keys) { def ->
var showDialog by remember { mutableStateOf(false) } ReorderSwitchPreference(def, defaultToolbarPref)
Preference(
name = def.title,
onClick = { showDialog = true },
)
if (showDialog) {
ToolbarKeyReorderDialog(
def.key,
defaultToolbarPref,
def.title,
) { showDialog = false }
}
}, },
PrefDef(context, Settings.PREF_PINNED_TOOLBAR_KEYS, R.string.pinned_toolbar_keys) { def -> PrefDef(context, Settings.PREF_PINNED_TOOLBAR_KEYS, R.string.pinned_toolbar_keys) { def ->
var showDialog by remember { mutableStateOf(false) } ReorderSwitchPreference(def, defaultPinnedToolbarPref)
Preference(
name = def.title,
onClick = { showDialog = true },
)
if (showDialog) {
ToolbarKeyReorderDialog(
def.key,
defaultPinnedToolbarPref,
def.title,
) { showDialog = false }
}
}, },
PrefDef(context, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, R.string.clipboard_toolbar_keys) { def -> PrefDef(context, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, R.string.clipboard_toolbar_keys) { def ->
var showDialog by remember { mutableStateOf(false) } ReorderSwitchPreference(def, defaultClipboardToolbarPref)
Preference(
name = def.title,
onClick = { showDialog = true },
)
if (showDialog) {
ToolbarKeyReorderDialog(
def.key,
defaultClipboardToolbarPref,
def.title,
) { showDialog = false }
}
}, },
PrefDef(context, NonSettingsPrefs.CUSTOM_KEY_CODES, R.string.customize_toolbar_key_codes) { def -> PrefDef(context, NonSettingsPrefs.CUSTOM_KEY_CODES, R.string.customize_toolbar_key_codes) { def ->
var showDialog by remember { mutableStateOf(false) } var showDialog by remember { mutableStateOf(false) }
@ -145,48 +113,6 @@ fun createToolbarPrefs(context: Context) = listOf(
} }
) )
@Composable
fun ToolbarKeyReorderDialog(
prefKey: String,
default: String,
title: String,
onDismiss: () -> Unit
) {
val ctx = LocalContext.current
val prefs = ctx.prefs()
val items = prefs.getString(prefKey, default)!!.split(";").mapTo(ArrayList()) {
val both = it.split(",")
KeyAndState(both.first(), both.last().toBoolean())
}
ReorderDialog(
onConfirmed = { reorderedItems ->
val value = reorderedItems.joinToString(";") { it.name + "," + it.state }
prefs.edit().putString(prefKey, value).apply()
keyboardNeedsReload = true
},
onDismissRequest = onDismiss,
onNeutral = { prefs.edit().remove(prefKey).apply() },
neutralButtonText = if (prefs.contains(prefKey)) stringResource(R.string.button_default) else null,
items = items,
title = { Text(title) },
displayItem = { item ->
var checked by remember { mutableStateOf(item.state) }
Row(verticalAlignment = Alignment.CenterVertically) {
KeyboardIconsSet.instance.GetIcon(item.name)
val text = item.name.lowercase().getStringResourceOrName("", ctx)
Text(text, Modifier.weight(1f))
Switch(
checked = checked,
onCheckedChange = { item.state = it; checked = it }
)
}
},
getKey = { it.name }
)
}
private class KeyAndState(var name: String, var state: Boolean)
@Preview @Preview
@Composable @Composable
private fun Preview() { private fun Preview() {