mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-02 12:52:15 +00:00
fix issues with loading / adding / removing user theme
This commit is contained in:
parent
589179e626
commit
767be15006
1 changed files with 111 additions and 89 deletions
|
@ -26,6 +26,7 @@ import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -43,11 +44,13 @@ import helium314.keyboard.keyboard.KeyboardTheme
|
||||||
import helium314.keyboard.latin.R
|
import helium314.keyboard.latin.R
|
||||||
import helium314.keyboard.latin.common.ColorType
|
import helium314.keyboard.latin.common.ColorType
|
||||||
import helium314.keyboard.latin.settings.Settings
|
import helium314.keyboard.latin.settings.Settings
|
||||||
|
import helium314.keyboard.latin.utils.Log
|
||||||
import helium314.keyboard.latin.utils.decodeBase36
|
import helium314.keyboard.latin.utils.decodeBase36
|
||||||
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.settings.Setting
|
import helium314.keyboard.settings.Setting
|
||||||
|
import helium314.keyboard.settings.SettingsActivity
|
||||||
import helium314.keyboard.settings.SettingsDestination
|
import helium314.keyboard.settings.SettingsDestination
|
||||||
import helium314.keyboard.settings.keyboardNeedsReload
|
import helium314.keyboard.settings.keyboardNeedsReload
|
||||||
import helium314.keyboard.settings.screens.SaveThoseColors
|
import helium314.keyboard.settings.screens.SaveThoseColors
|
||||||
|
@ -64,7 +67,11 @@ fun ColorThemePickerDialog(
|
||||||
) {
|
) {
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
val prefs = ctx.prefs()
|
val prefs = ctx.prefs()
|
||||||
val defaultColors = KeyboardTheme.getAvailableDefaultColors(prefs, false)
|
val b = (LocalContext.current.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState()
|
||||||
|
if ((b?.value ?: 0) < 0)
|
||||||
|
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
||||||
|
|
||||||
|
val defaultColors = KeyboardTheme.getAvailableDefaultColors(prefs, isNight)
|
||||||
|
|
||||||
// prefs.all is null in preview only
|
// prefs.all is null in preview only
|
||||||
val userColors = (prefs.all ?: mapOf(Settings.PREF_USER_COLORS_PREFIX + "usercolor" to "") ).keys.mapNotNull {
|
val userColors = (prefs.all ?: mapOf(Settings.PREF_USER_COLORS_PREFIX + "usercolor" to "") ).keys.mapNotNull {
|
||||||
|
@ -86,6 +93,7 @@ fun ColorThemePickerDialog(
|
||||||
if (index != -1) state.scrollToItem(index, -state.layoutInfo.viewportSize.height / 3)
|
if (index != -1) state.scrollToItem(index, -state.layoutInfo.viewportSize.height / 3)
|
||||||
}
|
}
|
||||||
var showLoadDialog by remember { mutableStateOf(false) }
|
var showLoadDialog by remember { mutableStateOf(false) }
|
||||||
|
val targetScreen = if (isNight) SettingsDestination.ColorsNight else SettingsDestination.Colors
|
||||||
ThreeButtonAlertDialog(
|
ThreeButtonAlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
cancelButtonText = stringResource(R.string.dialog_close),
|
cancelButtonText = stringResource(R.string.dialog_close),
|
||||||
|
@ -101,99 +109,24 @@ fun ColorThemePickerDialog(
|
||||||
LazyColumn(state = state) {
|
LazyColumn(state = state) {
|
||||||
items(colors) { item ->
|
items(colors) { item ->
|
||||||
if (item == "") {
|
if (item == "") {
|
||||||
var textValue by remember { mutableStateOf(TextFieldValue()) }
|
AddColorRow(onDismissRequest, userColors, targetScreen, setting.key)
|
||||||
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 {
|
} else {
|
||||||
Row(
|
ColorItemRow(onDismissRequest, item, item == selectedColor, item in userColors, targetScreen, setting.key)
|
||||||
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
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (showLoadDialog) {
|
var errorDialog by remember { mutableStateOf(false) }
|
||||||
var errorDialog by remember { mutableStateOf(false) }
|
val loadFilePicker = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
val loadFilePicker = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
|
||||||
if (it.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
|
val uri = it.data?.data ?: return@rememberLauncherForActivityResult
|
||||||
val uri = it.data?.data ?: return@rememberLauncherForActivityResult
|
ctx.getActivity()?.contentResolver?.openInputStream(uri)?.use {
|
||||||
ctx.getActivity()?.contentResolver?.openInputStream(uri)?.use {
|
errorDialog = !loadColorString(it.reader().readText(), prefs)
|
||||||
errorDialog = loadColorString(it.reader().readText(), prefs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (showLoadDialog) {
|
||||||
ConfirmationDialog(
|
ConfirmationDialog(
|
||||||
onDismissRequest = { showLoadDialog = false },
|
onDismissRequest = { showLoadDialog = false },
|
||||||
onConfirmed = {
|
onConfirmed = {
|
||||||
|
@ -205,18 +138,107 @@ fun ColorThemePickerDialog(
|
||||||
},
|
},
|
||||||
confirmButtonText = stringResource(R.string.button_load_custom),
|
confirmButtonText = stringResource(R.string.button_load_custom),
|
||||||
onNeutral = {
|
onNeutral = {
|
||||||
|
showLoadDialog = false
|
||||||
val cm = ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val cm = ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = cm.primaryClip?.takeIf { it.itemCount > 0 } ?: return@ConfirmationDialog
|
val clip = cm.primaryClip?.takeIf { it.itemCount > 0 } ?: return@ConfirmationDialog
|
||||||
val text = clip.getItemAt(0).text
|
val text = clip.getItemAt(0).text
|
||||||
errorDialog = loadColorString(text.toString(), prefs)
|
errorDialog = !loadColorString(text.toString(), prefs)
|
||||||
},
|
},
|
||||||
neutralButtonText = "load from clipboard" // todo: this is too long, maybe better if "load file" is changed to "load"?
|
neutralButtonText = "load from clipboard" // todo: this is too long, maybe better if "load file" is changed to "load"?
|
||||||
)
|
)
|
||||||
if (errorDialog)
|
}
|
||||||
InfoDialog("error") { showLoadDialog = false } // also error dialog to false?
|
if (errorDialog)
|
||||||
|
InfoDialog("error") { errorDialog = false }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AddColorRow(onDismissRequest: () -> Unit, userColors: Collection<String>, targetScreen: String, prefKey: String) {
|
||||||
|
var textValue by remember { mutableStateOf(TextFieldValue()) }
|
||||||
|
val prefs = LocalContext.current.prefs()
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(start = 10.dp)
|
||||||
|
) {
|
||||||
|
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(prefKey, textValue.text).apply()
|
||||||
|
SettingsDestination.navigateTo(targetScreen)
|
||||||
|
keyboardNeedsReload = true
|
||||||
|
}
|
||||||
|
) { Icon(painterResource(R.drawable.ic_edit), null) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ColorItemRow(onDismissRequest: () -> Unit, item: String, isSelected: Boolean, isUser: Boolean, targetScreen: String, prefKey: String) {
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
val prefs = ctx.prefs()
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable {
|
||||||
|
onDismissRequest()
|
||||||
|
prefs.edit().putString(prefKey, item).apply()
|
||||||
|
keyboardNeedsReload = true
|
||||||
|
}
|
||||||
|
.padding(start = 6.dp)
|
||||||
|
.heightIn(min = 40.dp)
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = isSelected,
|
||||||
|
onClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
prefs.edit().putString(prefKey, item).apply()
|
||||||
|
keyboardNeedsReload = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = item.getStringResourceOrName("theme_name_", ctx),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
if (isUser) {
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
IconButton(
|
||||||
|
onClick = { showDialog = true }
|
||||||
|
) { Icon(painterResource(R.drawable.ic_bin), null) }
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
prefs.edit().putString(prefKey, item).apply()
|
||||||
|
SettingsDestination.navigateTo(targetScreen)
|
||||||
|
keyboardNeedsReload = true
|
||||||
|
}
|
||||||
|
) { Icon(painterResource(R.drawable.ic_edit), null) }
|
||||||
|
if (showDialog)
|
||||||
|
ConfirmationDialog(
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
text = { Text(stringResource(R.string.delete_confirmation, item)) },
|
||||||
|
onConfirmed = {
|
||||||
|
showDialog = false
|
||||||
|
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 (isSelected)
|
||||||
|
prefs.edit().remove(prefKey).apply()
|
||||||
|
keyboardNeedsReload = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns whether the string was successfully deserialized and stored in prefs
|
||||||
private fun loadColorString(colorString: String, prefs: SharedPreferences): Boolean {
|
private fun loadColorString(colorString: String, prefs: SharedPreferences): Boolean {
|
||||||
try {
|
try {
|
||||||
val that = Json.decodeFromString<SaveThoseColors>(colorString)
|
val that = Json.decodeFromString<SaveThoseColors>(colorString)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue