mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-07 15:17:42 +00:00
add language screen (not finished)
This commit is contained in:
parent
12f1e20d9f
commit
85382de881
12 changed files with 709 additions and 20 deletions
|
@ -92,6 +92,9 @@ public final class Constants {
|
||||||
/** Overrides the general "more popups" setting */
|
/** Overrides the general "more popups" setting */
|
||||||
public static final String MORE_POPUPS = "MorePopups";
|
public static final String MORE_POPUPS = "MorePopups";
|
||||||
|
|
||||||
|
/** Overrides the general "localized number row" setting */
|
||||||
|
public static final String LOCALIZED_NUMBER_ROW = "LocalizedNumberRow";
|
||||||
|
|
||||||
private ExtraValue() {
|
private ExtraValue() {
|
||||||
// This utility class is not publicly instantiable.
|
// This utility class is not publicly instantiable.
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package helium314.keyboard.latin.utils
|
package helium314.keyboard.latin.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
import helium314.keyboard.latin.settings.Defaults.default
|
import helium314.keyboard.latin.settings.Defaults.default
|
||||||
import helium314.keyboard.latin.utils.LayoutType.Companion.folder
|
import helium314.keyboard.latin.utils.LayoutType.Companion.folder
|
||||||
import helium314.keyboard.latin.utils.ScriptUtils.script
|
import helium314.keyboard.latin.utils.ScriptUtils.script
|
||||||
|
@ -12,10 +13,11 @@ object LayoutUtils {
|
||||||
if (layoutType != LayoutType.MAIN)
|
if (layoutType != LayoutType.MAIN)
|
||||||
return context.assets.list(layoutType.folder)?.map { it.substringBefore(".") }.orEmpty()
|
return context.assets.list(layoutType.folder)?.map { it.substringBefore(".") }.orEmpty()
|
||||||
if (locale == null)
|
if (locale == null)
|
||||||
return SubtypeSettings.getAllAvailableSubtypes().mapTo(HashSet()) { it.mainLayoutName()?.substringBefore("+") ?: "qwerty" }
|
return SubtypeSettings.getAllAvailableSubtypes()
|
||||||
if (locale.script() == ScriptUtils.SCRIPT_LATIN)
|
|
||||||
return SubtypeSettings.getAllAvailableSubtypes().filter { it.isAsciiCapable && !LayoutUtilsCustom.isCustomLayout(it.mainLayoutName() ?: "qwerty") }
|
|
||||||
.mapTo(HashSet()) { it.mainLayoutName()?.substringBefore("+") ?: "qwerty" }
|
.mapTo(HashSet()) { it.mainLayoutName()?.substringBefore("+") ?: "qwerty" }
|
||||||
|
.apply { addAll(context.resources.getStringArray(R.array.predefined_layouts)) }
|
||||||
|
if (locale.script() == ScriptUtils.SCRIPT_LATIN)
|
||||||
|
return context.resources.getStringArray(R.array.predefined_layouts).toList()
|
||||||
return SubtypeSettings.getSubtypesForLocale(locale).mapNotNullTo(HashSet()) { it.mainLayoutName() }
|
return SubtypeSettings.getSubtypesForLocale(locale).mapNotNullTo(HashSet()) { it.mainLayoutName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAY
|
||||||
import helium314.keyboard.latin.common.LocaleUtils
|
import helium314.keyboard.latin.common.LocaleUtils
|
||||||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||||
import helium314.keyboard.latin.define.DebugFlags
|
import helium314.keyboard.latin.define.DebugFlags
|
||||||
|
import helium314.keyboard.latin.utils.LayoutType.Companion.toExtraValue
|
||||||
import helium314.keyboard.latin.utils.ScriptUtils.script
|
import helium314.keyboard.latin.utils.ScriptUtils.script
|
||||||
import org.xmlpull.v1.XmlPullParser
|
import org.xmlpull.v1.XmlPullParser
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -64,22 +65,23 @@ fun getResourceSubtypes(resources: Resources): List<InputMethodSubtype> {
|
||||||
|
|
||||||
/** Workaround for SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale ignoring custom layout names */
|
/** Workaround for SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale ignoring custom layout names */
|
||||||
// todo (later): this should be done properly and in SubtypeLocaleUtils
|
// todo (later): this should be done properly and in SubtypeLocaleUtils
|
||||||
fun InputMethodSubtype.displayName(context: Context): CharSequence {
|
fun InputMethodSubtype.displayName(context: Context): String {
|
||||||
val layoutName = SubtypeLocaleUtils.getMainLayoutName(this)
|
val layoutName = SubtypeLocaleUtils.getMainLayoutName(this)
|
||||||
if (LayoutUtilsCustom.isCustomLayout(layoutName))
|
if (LayoutUtilsCustom.isCustomLayout(layoutName))
|
||||||
return "${LocaleUtils.getLocaleDisplayNameInSystemLocale(locale(), context)} (${LayoutUtilsCustom.getDisplayName(layoutName)})"
|
return "${LocaleUtils.getLocaleDisplayNameInSystemLocale(locale(), context)} (${LayoutUtilsCustom.getDisplayName(layoutName)})"
|
||||||
return SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(this)
|
return SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SettingsSubtype(val locale: Locale, val extraValue: String) {
|
// some kind of intermediate between the string stored in preferences and an InputMethodSubtype
|
||||||
|
data class SettingsSubtype(val locale: Locale, val extraValues: String) {
|
||||||
|
|
||||||
fun toPref() = locale.toLanguageTag() + Separators.SET + extraValue
|
fun toPref() = locale.toLanguageTag() + Separators.SET + extraValues
|
||||||
|
|
||||||
/** Creates an additional subtype from the SettingsSubtype.
|
/** Creates an additional subtype from the SettingsSubtype.
|
||||||
* Resulting InputMethodSubtypes are equal if SettingsSubtypes are equal */
|
* Resulting InputMethodSubtypes are equal if SettingsSubtypes are equal */
|
||||||
fun toAdditionalSubtype(): InputMethodSubtype? {
|
fun toAdditionalSubtype(): InputMethodSubtype? {
|
||||||
val asciiCapable = locale.script() == ScriptUtils.SCRIPT_LATIN
|
val asciiCapable = locale.script() == ScriptUtils.SCRIPT_LATIN
|
||||||
val subtype = SubtypeUtilsAdditional.createAdditionalSubtype(locale, extraValue, asciiCapable, true)
|
val subtype = SubtypeUtilsAdditional.createAdditionalSubtype(locale, extraValues, asciiCapable, true)
|
||||||
if (subtype.nameResId == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT && !LayoutUtilsCustom.isCustomLayout(mainLayoutName() ?: "qwerty")) {
|
if (subtype.nameResId == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT && !LayoutUtilsCustom.isCustomLayout(mainLayoutName() ?: "qwerty")) {
|
||||||
// Skip unknown keyboard layout subtype. This may happen when predefined keyboard
|
// Skip unknown keyboard layout subtype. This may happen when predefined keyboard
|
||||||
// layout has been removed.
|
// layout has been removed.
|
||||||
|
@ -89,7 +91,40 @@ data class SettingsSubtype(val locale: Locale, val extraValue: String) {
|
||||||
return subtype
|
return subtype
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mainLayoutName() = LayoutType.getMainLayoutFromExtraValue(extraValue)
|
fun mainLayoutName() = LayoutType.getMainLayoutFromExtraValue(extraValues)
|
||||||
|
|
||||||
|
fun layoutName(type: LayoutType) = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")[type]
|
||||||
|
|
||||||
|
fun with(extraValueKey: String, extraValue: String?): SettingsSubtype {
|
||||||
|
val newList = extraValues.split(",")
|
||||||
|
.filterNot { it.startsWith("$extraValueKey=") || it == extraValueKey }
|
||||||
|
val newValue = if (extraValue == null) extraValueKey else "$extraValueKey=$extraValue"
|
||||||
|
val newValues = (newList + newValue).joinToString(",")
|
||||||
|
return copy(extraValues = newValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun without(extraValueKey: String): SettingsSubtype {
|
||||||
|
val newValues = extraValues.split(",")
|
||||||
|
.filterNot { it.startsWith("$extraValueKey=") || it == extraValueKey }
|
||||||
|
.joinToString(",")
|
||||||
|
return copy(extraValues = newValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getExtraValueOf(extraValueKey: String): String? = extraValues.split(",")
|
||||||
|
.firstOrNull { it.startsWith("$extraValueKey=") }?.substringAfter("$extraValueKey=")
|
||||||
|
|
||||||
|
fun withLayout(type: LayoutType, name: String): SettingsSubtype {
|
||||||
|
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
|
||||||
|
map[type] = name
|
||||||
|
return with(KEYBOARD_LAYOUT_SET, map.toExtraValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withoutLayout(type: LayoutType): SettingsSubtype {
|
||||||
|
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
|
||||||
|
map.remove(type)
|
||||||
|
return if (map.isEmpty()) without(KEYBOARD_LAYOUT_SET)
|
||||||
|
else with(KEYBOARD_LAYOUT_SET, map.toExtraValue())
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun String.toSettingsSubtype() =
|
fun String.toSettingsSubtype() =
|
||||||
|
@ -109,6 +144,8 @@ data class SettingsSubtype(val locale: Locale, val extraValue: String) {
|
||||||
// todo: this is in "old" additional subtypes, but where was it set?
|
// todo: this is in "old" additional subtypes, but where was it set?
|
||||||
// must have been by app in 2.3, but not any more?
|
// must have been by app in 2.3, but not any more?
|
||||||
// anyway, a. we can easily create it again, and b. it may contain "bad" characters messing up the extra value
|
// anyway, a. we can easily create it again, and b. it may contain "bad" characters messing up the extra value
|
||||||
|
// removing UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME changes the name of some layouts,
|
||||||
|
// e.g. from "English (United States)" to "English (US)"
|
||||||
|| it.startsWith(ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
|
|| it.startsWith(ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
|
||||||
}.joinToString(",")
|
}.joinToString(",")
|
||||||
require(!filteredExtraValue.contains(Separators.SETS) && !filteredExtraValue.contains(Separators.SET))
|
require(!filteredExtraValue.contains(Separators.SETS) && !filteredExtraValue.contains(Separators.SET))
|
||||||
|
|
|
@ -51,6 +51,7 @@ object SubtypeUtilsAdditional {
|
||||||
fun createEmojiCapableAdditionalSubtype(locale: Locale, mainLayoutName: String, asciiCapable: Boolean) =
|
fun createEmojiCapableAdditionalSubtype(locale: Locale, mainLayoutName: String, asciiCapable: Boolean) =
|
||||||
createAdditionalSubtype(locale, "${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN${Separators.KV}$mainLayoutName", asciiCapable, true)
|
createAdditionalSubtype(locale, "${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN${Separators.KV}$mainLayoutName", asciiCapable, true)
|
||||||
|
|
||||||
|
// todo: consider using SettingsSubtype
|
||||||
fun addAdditionalSubtype(prefs: SharedPreferences, subtype: InputMethodSubtype) {
|
fun addAdditionalSubtype(prefs: SharedPreferences, subtype: InputMethodSubtype) {
|
||||||
val oldAdditionalSubtypesString = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
|
val oldAdditionalSubtypesString = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
|
||||||
val additionalSubtypes = createAdditionalSubtypes(oldAdditionalSubtypesString).toMutableSet()
|
val additionalSubtypes = createAdditionalSubtypes(oldAdditionalSubtypesString).toMutableSet()
|
||||||
|
@ -67,6 +68,27 @@ object SubtypeUtilsAdditional {
|
||||||
Settings.writePrefAdditionalSubtypes(prefs, newAdditionalSubtypesString)
|
Settings.writePrefAdditionalSubtypes(prefs, newAdditionalSubtypesString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updates additional subtypes, enabled subtypes, and selected subtype
|
||||||
|
fun changeAdditionalSubtype(from: SettingsSubtype, to: SettingsSubtype, prefs: SharedPreferences) {
|
||||||
|
val new = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
|
||||||
|
.split(Separators.SETS).mapTo(sortedSetOf()) {
|
||||||
|
if (it == from.toPref()) to.toPref() else it
|
||||||
|
}
|
||||||
|
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, new.joinToString(Separators.SETS)).apply()
|
||||||
|
|
||||||
|
val fromSubtype = from.toAdditionalSubtype() // will be null if we edit a resource subtype
|
||||||
|
val toSubtype = to.toAdditionalSubtype() // should never be null
|
||||||
|
if (SubtypeSettings.getSelectedSubtype(prefs) == fromSubtype && toSubtype != null) {
|
||||||
|
SubtypeSettings.setSelectedSubtype(prefs, toSubtype)
|
||||||
|
}
|
||||||
|
if (SubtypeSettings.getEnabledSubtypes(prefs, false).contains(fromSubtype)) {
|
||||||
|
if (fromSubtype != null)
|
||||||
|
SubtypeSettings.removeEnabledSubtype(prefs, fromSubtype)
|
||||||
|
if (toSubtype != null)
|
||||||
|
SubtypeSettings.addEnabledSubtype(prefs, toSubtype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createAdditionalSubtypes(prefSubtypes: String): List<InputMethodSubtype> {
|
fun createAdditionalSubtypes(prefSubtypes: String): List<InputMethodSubtype> {
|
||||||
if (prefSubtypes.isEmpty())
|
if (prefSubtypes.isEmpty())
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|
|
@ -16,9 +16,10 @@ import helium314.keyboard.settings.screens.AppearanceScreen
|
||||||
import helium314.keyboard.settings.screens.ColorsScreen
|
import helium314.keyboard.settings.screens.ColorsScreen
|
||||||
import helium314.keyboard.settings.screens.DebugScreen
|
import helium314.keyboard.settings.screens.DebugScreen
|
||||||
import helium314.keyboard.settings.screens.GestureTypingScreen
|
import helium314.keyboard.settings.screens.GestureTypingScreen
|
||||||
import helium314.keyboard.settings.screens.LayoutScreen
|
import helium314.keyboard.settings.screens.LanguageScreen
|
||||||
import helium314.keyboard.settings.screens.MainSettingsScreen
|
import helium314.keyboard.settings.screens.MainSettingsScreen
|
||||||
import helium314.keyboard.settings.screens.PreferencesScreen
|
import helium314.keyboard.settings.screens.PreferencesScreen
|
||||||
|
import helium314.keyboard.settings.screens.SecondaryLayoutScreen
|
||||||
import helium314.keyboard.settings.screens.TextCorrectionScreen
|
import helium314.keyboard.settings.screens.TextCorrectionScreen
|
||||||
import helium314.keyboard.settings.screens.ToolbarScreen
|
import helium314.keyboard.settings.screens.ToolbarScreen
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -92,10 +93,10 @@ fun SettingsNavHost(
|
||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
composable(SettingsDestination.Languages) {
|
composable(SettingsDestination.Languages) {
|
||||||
// LanguageScreen(onClickBack = ::goBack)
|
LanguageScreen(onClickBack = ::goBack)
|
||||||
}
|
}
|
||||||
composable(SettingsDestination.Layouts) {
|
composable(SettingsDestination.Layouts) {
|
||||||
LayoutScreen(onClickBack = ::goBack)
|
SecondaryLayoutScreen(onClickBack = ::goBack)
|
||||||
}
|
}
|
||||||
composable(SettingsDestination.Colors) {
|
composable(SettingsDestination.Colors) {
|
||||||
ColorsScreen(isNight = false, onClickBack = ::goBack)
|
ColorsScreen(isNight = false, onClickBack = ::goBack)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
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.LocalTextStyle
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
// modified version of ListPickerDialog for selecting multiple items
|
||||||
|
@Composable
|
||||||
|
fun <T: Any> MultiListPickerDialog(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
items: List<T>,
|
||||||
|
onConfirmed: (Collection<T>) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
title: (@Composable () -> Unit)? = null,
|
||||||
|
initialSelection: List<T> = emptyList(),
|
||||||
|
getItemName: (@Composable (T) -> String) = { it.toString() },
|
||||||
|
) {
|
||||||
|
var selected by remember { mutableStateOf(initialSelection.toSet()) }
|
||||||
|
val state = rememberLazyListState()
|
||||||
|
|
||||||
|
ThreeButtonAlertDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onConfirmed = { onConfirmed(selected) },
|
||||||
|
confirmButtonText = stringResource(android.R.string.ok),
|
||||||
|
modifier = modifier,
|
||||||
|
title = title,
|
||||||
|
text = {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalTextStyle provides MaterialTheme.typography.bodyLarge
|
||||||
|
) {
|
||||||
|
LazyColumn(state = state) {
|
||||||
|
items(items) { item ->
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable {
|
||||||
|
selected = if (item in selected) selected - item else selected + item
|
||||||
|
}
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.heightIn(min = 40.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = getItemName(item),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
Switch(
|
||||||
|
checked = item in selected,
|
||||||
|
onCheckedChange = {
|
||||||
|
selected = if (it) selected + item else selected - item
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun Preview() {
|
||||||
|
val items = remember { (0..<5).toList() }
|
||||||
|
MultiListPickerDialog(
|
||||||
|
onDismissRequest = {},
|
||||||
|
items = items,
|
||||||
|
onConfirmed = {},
|
||||||
|
title = { Text("Select something") },
|
||||||
|
initialSelection = listOf(2, 4),
|
||||||
|
getItemName = { "Item $it" },
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,362 @@
|
||||||
|
package helium314.keyboard.settings.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.inputmethod.InputMethodSubtype
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import helium314.keyboard.keyboard.internal.KeyboardIconsSet
|
||||||
|
import helium314.keyboard.keyboard.internal.keyboard_parser.hasLocalizedNumberRow
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
|
import helium314.keyboard.latin.common.Constants.Separators
|
||||||
|
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue
|
||||||
|
import helium314.keyboard.latin.common.LocaleUtils
|
||||||
|
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||||
|
import helium314.keyboard.latin.settings.Defaults
|
||||||
|
import helium314.keyboard.latin.settings.Settings
|
||||||
|
import helium314.keyboard.latin.utils.LayoutType
|
||||||
|
import helium314.keyboard.latin.utils.LayoutType.Companion.displayNameId
|
||||||
|
import helium314.keyboard.latin.utils.LayoutUtils
|
||||||
|
import helium314.keyboard.latin.utils.LayoutUtilsCustom
|
||||||
|
import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_LATIN
|
||||||
|
import helium314.keyboard.latin.utils.ScriptUtils.script
|
||||||
|
import helium314.keyboard.latin.utils.SettingsSubtype
|
||||||
|
import helium314.keyboard.latin.utils.SettingsSubtype.Companion.toSettingsSubtype
|
||||||
|
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
|
||||||
|
import helium314.keyboard.latin.utils.SubtypeSettings
|
||||||
|
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
|
||||||
|
import helium314.keyboard.latin.utils.getDictionaryLocales
|
||||||
|
import helium314.keyboard.latin.utils.getStringResourceOrName
|
||||||
|
import helium314.keyboard.latin.utils.prefs
|
||||||
|
import helium314.keyboard.settings.screens.GetIcon
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
// todo:
|
||||||
|
// save when "editing" a resource subtypes is not working
|
||||||
|
// default buttons missing
|
||||||
|
// string resources
|
||||||
|
@Composable
|
||||||
|
fun SubtypeDialog(
|
||||||
|
// could also use InputMethodSubtype if there is any advantage
|
||||||
|
// but as soon as anything is changed we will need an additional subtype anyway...
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
subtype: InputMethodSubtype,
|
||||||
|
onConfirmed: (SettingsSubtype) -> Unit,
|
||||||
|
) {
|
||||||
|
// todo: make sure the values are always correct (e.g. if using rememberSaveable and rotating)
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
val prefs = ctx.prefs()
|
||||||
|
var currentSubtype by remember { mutableStateOf(subtype.toSettingsSubtype()) }
|
||||||
|
val availableLocalesForScript = getAvailableSecondaryLocales(ctx, currentSubtype.locale).sortedBy { it.toLanguageTag() }
|
||||||
|
var showSecondaryLocaleDialog by remember { mutableStateOf(false) }
|
||||||
|
var showKeyOrderDialog by remember { mutableStateOf(false) }
|
||||||
|
var showHintOrderDialog by remember { mutableStateOf(false) }
|
||||||
|
var showMorePopupsDialog by remember { mutableStateOf(false) }
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
ThreeButtonAlertDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onConfirmed = { onConfirmed(currentSubtype) },
|
||||||
|
neutralButtonText = if (SubtypeSettings.isAdditionalSubtype(subtype)) null else stringResource(R.string.delete),
|
||||||
|
onNeutral = {
|
||||||
|
SubtypeUtilsAdditional.removeAdditionalSubtype(prefs, subtype)
|
||||||
|
SubtypeSettings.removeEnabledSubtype(prefs, subtype)
|
||||||
|
|
||||||
|
}, // maybe confirm dialog?
|
||||||
|
title = { Text(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)) },
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.verticalScroll(scrollState),
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
WithDescription("main layout") {
|
||||||
|
val appLayouts = LayoutUtils.getAvailableLayouts(LayoutType.MAIN, ctx, currentSubtype.locale)
|
||||||
|
val customLayouts = LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, ctx, currentSubtype.locale).map { it.name }
|
||||||
|
DropDownField(
|
||||||
|
items = appLayouts + customLayouts,
|
||||||
|
selectedItem = currentSubtype.mainLayoutName() ?: "qwerty", // todo: what about qwerty+ and similar?
|
||||||
|
onSelected = {
|
||||||
|
currentSubtype = currentSubtype.withLayout(LayoutType.MAIN, it)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
// todo: displayName can be complicated and may require an inputmehtodsubtype...
|
||||||
|
// maybe search for stuff in resource subtypes?
|
||||||
|
Text(it)
|
||||||
|
// todo: edit button? or only for selected layout? and delete button?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WithDescription(stringResource(R.string.secondary_locale)) {
|
||||||
|
TextButton(onClick = { showSecondaryLocaleDialog = true }, Modifier.fillMaxWidth()) {
|
||||||
|
val text = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)
|
||||||
|
?.split(Separators.KV)?.joinToString(", ") {
|
||||||
|
LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx)
|
||||||
|
} ?: "none"
|
||||||
|
Text(text, style = MaterialTheme.typography.bodyLarge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextButton(onClick = { showSecondaryLocaleDialog = true }) {
|
||||||
|
val text = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)
|
||||||
|
?.split(Separators.KV)?.joinToString(", ") {
|
||||||
|
LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx)
|
||||||
|
} ?: ""
|
||||||
|
Column(Modifier.fillMaxWidth()) {
|
||||||
|
Text(stringResource(R.string.secondary_locale))
|
||||||
|
Text(text, style = MaterialTheme.typography.bodyLarge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WithDescription("dictionaries") {
|
||||||
|
// todo: maybe remove here and use a separate screen for dictionary management
|
||||||
|
// would be clearer, as dicts are per language (and no intention to change it)
|
||||||
|
Text("not yet implemented")
|
||||||
|
}
|
||||||
|
TextButton(onClick = { showKeyOrderDialog = true })
|
||||||
|
{ Text(stringResource(R.string.popup_order), Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge) }
|
||||||
|
TextButton(onClick = { showHintOrderDialog = true })
|
||||||
|
{ Text(stringResource(R.string.hint_source), Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge) }
|
||||||
|
if (currentSubtype.locale.script() == SCRIPT_LATIN)
|
||||||
|
WithDescription(stringResource(R.string.show_popup_keys_title)) {
|
||||||
|
val explicitValue = currentSubtype.getExtraValueOf(ExtraValue.MORE_POPUPS)
|
||||||
|
val value = explicitValue ?: prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS)
|
||||||
|
val textResId = when (value) { // todo: this should not be duplicated... see below
|
||||||
|
"normal" -> R.string.show_popup_keys_normal
|
||||||
|
"more" -> R.string.show_popup_keys_more
|
||||||
|
"all" -> R.string.show_popup_keys_all
|
||||||
|
else -> R.string.show_popup_keys_main
|
||||||
|
}
|
||||||
|
TextButton(onClick = { showMorePopupsDialog = true }, Modifier.fillMaxWidth())
|
||||||
|
{ Text(stringResource(textResId)) }
|
||||||
|
}
|
||||||
|
if (hasLocalizedNumberRow(currentSubtype.locale, ctx))
|
||||||
|
Row {
|
||||||
|
Text(stringResource(R.string.localized_number_row), Modifier.weight(1f))
|
||||||
|
Switch(
|
||||||
|
checked = currentSubtype.getExtraValueOf(ExtraValue.LOCALIZED_NUMBER_ROW)?.toBoolean()
|
||||||
|
?: prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, Defaults.PREF_LOCALIZED_NUMBER_ROW),
|
||||||
|
onCheckedChange = {
|
||||||
|
currentSubtype = currentSubtype.with(ExtraValue.LOCALIZED_NUMBER_ROW, it.toString())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// todo: default button?
|
||||||
|
}
|
||||||
|
LayoutType.entries.forEach { type ->
|
||||||
|
if (type == LayoutType.MAIN) return@forEach
|
||||||
|
// todo: also some default button, to be shown when necessary, uses currentSubtype.withoutLayout(type)
|
||||||
|
WithDescription(stringResource(type.displayNameId)) {
|
||||||
|
val explicitLayout = currentSubtype.layoutName(type)
|
||||||
|
val layout = explicitLayout ?: Settings.readDefaultLayoutName(type, prefs)
|
||||||
|
val defaultLayouts = LayoutUtils.getAvailableLayouts(type, ctx)
|
||||||
|
val customLayouts = LayoutUtilsCustom.getLayoutFiles(type, ctx).map { it.name }
|
||||||
|
DropDownField(
|
||||||
|
items = defaultLayouts + customLayouts,
|
||||||
|
selectedItem = layout,
|
||||||
|
onSelected = {
|
||||||
|
currentSubtype = currentSubtype.withLayout(type, it)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val displayName = if (LayoutUtilsCustom.isCustomLayout(it)) LayoutUtilsCustom.getDisplayName(it)
|
||||||
|
else it.getStringResourceOrName("layout_", ctx)
|
||||||
|
Text(displayName)
|
||||||
|
// content is name, and if it's user layout there is an edit button
|
||||||
|
// also maybe there should be an "add" button similar to the old settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (showSecondaryLocaleDialog)
|
||||||
|
MultiListPickerDialog(
|
||||||
|
onDismissRequest = { showSecondaryLocaleDialog = false },
|
||||||
|
onConfirmed = {
|
||||||
|
val newValue = it.joinToString(Separators.KV) { it.toLanguageTag() }
|
||||||
|
currentSubtype = if (newValue.isEmpty()) currentSubtype.without(ExtraValue.SECONDARY_LOCALES)
|
||||||
|
else currentSubtype.with(ExtraValue.SECONDARY_LOCALES, newValue)
|
||||||
|
},
|
||||||
|
items = availableLocalesForScript,
|
||||||
|
initialSelection = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)
|
||||||
|
?.split(Separators.KV)?.map { it.constructLocale() }.orEmpty(),
|
||||||
|
getItemName = { LocaleUtils.getLocaleDisplayNameInSystemLocale(it, ctx) }
|
||||||
|
)
|
||||||
|
if (showKeyOrderDialog) {
|
||||||
|
val setting = currentSubtype.getExtraValueOf(ExtraValue.POPUP_ORDER)
|
||||||
|
PopupOrderDialog(
|
||||||
|
onDismissRequest = { showKeyOrderDialog = false },
|
||||||
|
initialValue = setting ?: prefs.getString(Settings.PREF_POPUP_KEYS_ORDER, Defaults.PREF_POPUP_KEYS_ORDER)!!,
|
||||||
|
title = stringResource(R.string.popup_order),
|
||||||
|
showDefault = setting != null,
|
||||||
|
onConfirmed = {
|
||||||
|
if (it == null) currentSubtype = currentSubtype.without(ExtraValue.POPUP_ORDER)
|
||||||
|
else currentSubtype = currentSubtype.with(ExtraValue.POPUP_ORDER, it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showHintOrderDialog) {
|
||||||
|
val setting = currentSubtype.getExtraValueOf(ExtraValue.HINT_ORDER)
|
||||||
|
PopupOrderDialog(
|
||||||
|
onDismissRequest = { showHintOrderDialog = false },
|
||||||
|
initialValue = setting ?: prefs.getString(Settings.PREF_POPUP_KEYS_LABELS_ORDER, Defaults.PREF_POPUP_KEYS_LABELS_ORDER)!!,
|
||||||
|
title = stringResource(R.string.hint_source),
|
||||||
|
showDefault = setting != null,
|
||||||
|
onConfirmed = {
|
||||||
|
if (it == null) currentSubtype = currentSubtype.without(ExtraValue.HINT_ORDER)
|
||||||
|
else currentSubtype = currentSubtype.with(ExtraValue.HINT_ORDER, it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showMorePopupsDialog) {
|
||||||
|
// todo: default button in here? or next to the pref?
|
||||||
|
val items = listOf("normal", "main", "more", "all")
|
||||||
|
val explicitValue = currentSubtype.getExtraValueOf(ExtraValue.MORE_POPUPS)
|
||||||
|
val value = explicitValue ?: prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS)
|
||||||
|
ListPickerDialog(
|
||||||
|
onDismissRequest = { showMorePopupsDialog = false },
|
||||||
|
items = items,
|
||||||
|
getItemName = {
|
||||||
|
val textResId = when (it) { // todo: this should not be duplicated... now we have it twice here, and in advanced settings
|
||||||
|
"normal" -> R.string.show_popup_keys_normal
|
||||||
|
"more" -> R.string.show_popup_keys_more
|
||||||
|
"all" -> R.string.show_popup_keys_all
|
||||||
|
else -> R.string.show_popup_keys_main
|
||||||
|
}
|
||||||
|
stringResource(textResId)
|
||||||
|
},
|
||||||
|
selectedItem = value,
|
||||||
|
onItemSelected = { currentSubtype = currentSubtype.with(ExtraValue.MORE_POPUPS, it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// from ReorderSwitchPreference
|
||||||
|
@Composable
|
||||||
|
private fun PopupOrderDialog(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
initialValue: String,
|
||||||
|
onConfirmed: (String?) -> Unit,
|
||||||
|
title: String,
|
||||||
|
showDefault: Boolean
|
||||||
|
) {
|
||||||
|
class KeyAndState(var name: String, var state: Boolean)
|
||||||
|
val items = initialValue.split(Separators.ENTRY).map {
|
||||||
|
KeyAndState(it.substringBefore(Separators.KV), it.substringAfter(Separators.KV).toBoolean())
|
||||||
|
}
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
ReorderDialog(
|
||||||
|
onConfirmed = { reorderedItems ->
|
||||||
|
val value = reorderedItems.joinToString(Separators.ENTRY) { it.name + Separators.KV + it.state }
|
||||||
|
onConfirmed(value)
|
||||||
|
},
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onNeutral = { onDismissRequest(); onConfirmed(null) },
|
||||||
|
neutralButtonText = if (showDefault) stringResource(R.string.button_default) else null,
|
||||||
|
items = items,
|
||||||
|
title = { Text(title) },
|
||||||
|
displayItem = { item ->
|
||||||
|
var checked by rememberSaveable { 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
|
||||||
|
private fun WithDescription(
|
||||||
|
description: String,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Text(description, style = MaterialTheme.typography.bodySmall)
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun <T>DropDownField(
|
||||||
|
items: List<T>,
|
||||||
|
selectedItem: T,
|
||||||
|
onSelected: (T) -> Unit,
|
||||||
|
itemContent: @Composable (T) -> Unit,
|
||||||
|
) {
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
Box(
|
||||||
|
Modifier.clickable { expanded = !expanded }
|
||||||
|
//.border(2.dp, MaterialTheme.colorScheme.onSecondary)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 4.dp)
|
||||||
|
) {
|
||||||
|
Box(Modifier.weight(1f)) {
|
||||||
|
itemContent(selectedItem)
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = { expanded = !expanded },
|
||||||
|
enabled = items.size > 1
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painterResource(R.drawable.ic_arrow_left),
|
||||||
|
null,
|
||||||
|
Modifier.rotate(-90f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false }
|
||||||
|
) {
|
||||||
|
items.forEach {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { itemContent(it) },
|
||||||
|
onClick = { expanded = false; onSelected(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get locales with same script as main locale, but different language
|
||||||
|
// todo: do we need any sort of force-ascii like in old variant?
|
||||||
|
// now we use hi-Latn and sr-Latn for the relevant subtypes, so it should be fine
|
||||||
|
// only potential issue is the Latn-default if we don't have the script for a locale,
|
||||||
|
// but in that case we should rather add the script to ScriptUtils
|
||||||
|
private fun getAvailableSecondaryLocales(context: Context, mainLocale: Locale): List<Locale> {
|
||||||
|
val locales = getDictionaryLocales(context)
|
||||||
|
locales.removeAll {
|
||||||
|
// it.language == mainLocale.language || it.script() != mainLocale.script()
|
||||||
|
it == mainLocale || it.script() != mainLocale.script() // todo: check whether this is fine, otherwise go back to the variant above
|
||||||
|
}
|
||||||
|
return locales.toList()
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package helium314.keyboard.settings.screens
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.inputmethod.InputMethodSubtype
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
|
import helium314.keyboard.latin.common.Constants.Separators
|
||||||
|
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue
|
||||||
|
import helium314.keyboard.latin.common.LocaleUtils
|
||||||
|
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||||
|
import helium314.keyboard.latin.common.splitOnWhitespace
|
||||||
|
import helium314.keyboard.latin.settings.Defaults
|
||||||
|
import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX
|
||||||
|
import helium314.keyboard.latin.utils.DictionaryInfoUtils
|
||||||
|
import helium314.keyboard.latin.utils.Log
|
||||||
|
import helium314.keyboard.latin.utils.SettingsSubtype.Companion.toSettingsSubtype
|
||||||
|
import helium314.keyboard.latin.utils.SubtypeSettings
|
||||||
|
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
|
||||||
|
import helium314.keyboard.latin.utils.displayName
|
||||||
|
import helium314.keyboard.latin.utils.getActivity
|
||||||
|
import helium314.keyboard.latin.utils.locale
|
||||||
|
import helium314.keyboard.latin.utils.prefs
|
||||||
|
import helium314.keyboard.settings.SearchScreen
|
||||||
|
import helium314.keyboard.settings.SettingsActivity
|
||||||
|
import helium314.keyboard.settings.dialogs.SubtypeDialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LanguageScreen(
|
||||||
|
onClickBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
var sortedSubtypes by remember { mutableStateOf(getSortedSubtypes(ctx)) }
|
||||||
|
val prefs = ctx.prefs()
|
||||||
|
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")
|
||||||
|
var selectedSubtype: InputMethodSubtype? by remember { mutableStateOf(null) } // todo: rememberSaveable? maybe with SettingsSubtype?
|
||||||
|
SearchScreen(
|
||||||
|
onClickBack = onClickBack,
|
||||||
|
title = {
|
||||||
|
Column {
|
||||||
|
Text(stringResource(R.string.language_and_layouts_title))
|
||||||
|
Text(stringResource(
|
||||||
|
R.string.text_tap_languages),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filteredItems = { term ->
|
||||||
|
// todo: maybe better performance with display name cache?
|
||||||
|
sortedSubtypes.filter {
|
||||||
|
it.displayName(ctx).replace("(", "")
|
||||||
|
.splitOnWhitespace().any { it.startsWith(term, true) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemContent = { item ->
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { selectedSubtype = item }
|
||||||
|
.padding(vertical = 6.dp, horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text(item.displayName(ctx), style = MaterialTheme.typography.bodyLarge)
|
||||||
|
val description = item.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)?.split(Separators.KV)
|
||||||
|
?.joinToString(", ") { LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx) }
|
||||||
|
if (description != null)
|
||||||
|
Text(
|
||||||
|
text = description,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = item in SubtypeSettings.getEnabledSubtypes(prefs),
|
||||||
|
onCheckedChange = {
|
||||||
|
if (it) SubtypeSettings.addEnabledSubtype(prefs, item)
|
||||||
|
else SubtypeSettings.removeEnabledSubtype(prefs, item)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (selectedSubtype != null) {
|
||||||
|
val oldSubtype = selectedSubtype!!
|
||||||
|
SubtypeDialog(
|
||||||
|
onDismissRequest = { selectedSubtype = null },
|
||||||
|
onConfirmed = {
|
||||||
|
// todo: this does not work when "modifying" a resource subtype
|
||||||
|
SubtypeUtilsAdditional.changeAdditionalSubtype(oldSubtype.toSettingsSubtype(), it, prefs)
|
||||||
|
sortedSubtypes = getSortedSubtypes(ctx)
|
||||||
|
},
|
||||||
|
subtype = oldSubtype
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: sorting is slow, need to cache displayName (overall or just in getSortedSubtypes), and then it should be fine
|
||||||
|
private fun getSortedSubtypes(context: Context): List<InputMethodSubtype> {
|
||||||
|
val systemLocales = SubtypeSettings.getSystemLocales()
|
||||||
|
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(context.prefs(), true)
|
||||||
|
val localesWithDictionary = DictionaryInfoUtils.getCachedDirectoryList(context)?.mapNotNull { dir ->
|
||||||
|
if (!dir.isDirectory)
|
||||||
|
return@mapNotNull null
|
||||||
|
if (dir.list()?.any { it.endsWith(USER_DICTIONARY_SUFFIX) } == true)
|
||||||
|
dir.name.constructLocale()
|
||||||
|
else null
|
||||||
|
}.orEmpty()
|
||||||
|
|
||||||
|
val defaultAdditionalSubtypes = Defaults.PREF_ADDITIONAL_SUBTYPES.split(Separators.SETS).map {
|
||||||
|
it.substringBefore(Separators.SET) to (it.substringAfter(Separators.SET) + ",AsciiCapable,EmojiCapable,isAdditionalSubtype")
|
||||||
|
}
|
||||||
|
fun isDefaultSubtype(subtype: InputMethodSubtype): Boolean =
|
||||||
|
defaultAdditionalSubtypes.any { it.first == subtype.locale().language && it.second == subtype.extraValue }
|
||||||
|
|
||||||
|
val subtypeSortComparator = compareBy<InputMethodSubtype>(
|
||||||
|
{ it !in enabledSubtypes },
|
||||||
|
{ it.locale() !in localesWithDictionary },
|
||||||
|
{ it.locale() !in systemLocales},
|
||||||
|
{ !(SubtypeSettings.isAdditionalSubtype(it) && !isDefaultSubtype(it) ) },
|
||||||
|
{
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) it.languageTag == "zz"
|
||||||
|
else it.locale == "zz"
|
||||||
|
},
|
||||||
|
{ it.displayName(context) }
|
||||||
|
)
|
||||||
|
return SubtypeSettings.getAllAvailableSubtypes().sortedWith(subtypeSortComparator)
|
||||||
|
}
|
|
@ -23,7 +23,10 @@ import helium314.keyboard.latin.settings.LanguageSettingsFragment
|
||||||
import helium314.keyboard.latin.settings.PreferencesSettingsFragment
|
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.SubtypeSettings
|
||||||
|
import helium314.keyboard.latin.utils.displayName
|
||||||
import helium314.keyboard.latin.utils.getActivity
|
import helium314.keyboard.latin.utils.getActivity
|
||||||
|
import helium314.keyboard.latin.utils.prefs
|
||||||
import helium314.keyboard.latin.utils.switchTo
|
import helium314.keyboard.latin.utils.switchTo
|
||||||
import helium314.keyboard.settings.preferences.Preference
|
import helium314.keyboard.settings.preferences.Preference
|
||||||
import helium314.keyboard.settings.preferences.PreferenceCategory
|
import helium314.keyboard.settings.preferences.PreferenceCategory
|
||||||
|
@ -49,9 +52,11 @@ fun MainSettingsScreen(
|
||||||
title = stringResource(R.string.ime_settings),
|
title = stringResource(R.string.ime_settings),
|
||||||
settings = emptyList(),
|
settings = emptyList(),
|
||||||
) {
|
) {
|
||||||
|
val enabledSubtypes = SubtypeSettings.getEnabledSubtypes(ctx.prefs(), true)
|
||||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||||
Preference(
|
Preference(
|
||||||
name = stringResource(R.string.language_and_layouts_title),
|
name = stringResource(R.string.language_and_layouts_title),
|
||||||
|
description = enabledSubtypes.joinToString(", ") { it.displayName(ctx) },
|
||||||
onClick = onClickLanguage,
|
onClick = onClickLanguage,
|
||||||
icon = R.drawable.ic_settings_languages_foreground
|
icon = R.drawable.ic_settings_languages_foreground
|
||||||
) {
|
) {
|
||||||
|
@ -118,7 +123,7 @@ fun MainSettingsScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Preference(
|
Preference(
|
||||||
name = stringResource(R.string.keyboard_layout_set),
|
name = stringResource(R.string.settings_screen_secondary_layouts),
|
||||||
onClick = onClickLayouts,
|
onClick = onClickLayouts,
|
||||||
icon = R.drawable.ic_ime_switcher
|
icon = R.drawable.ic_ime_switcher
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -28,20 +28,16 @@ import helium314.keyboard.settings.preferences.Preference
|
||||||
fun SecondaryLayoutScreen(
|
fun SecondaryLayoutScreen(
|
||||||
onClickBack: () -> Unit,
|
onClickBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
// todo: enable main layouts
|
// no main layouts in here
|
||||||
// which layouts to show? all is too much, maybe limit to latin and layouts for enabled locales (and system locales?)
|
// could be added later, but need to decide how to do it (showing all main layouts is too much)
|
||||||
SearchSettingsScreen(
|
SearchSettingsScreen(
|
||||||
onClickBack = onClickBack,
|
onClickBack = onClickBack,
|
||||||
title = stringResource(R.string.keyboard_layout_set),
|
title = stringResource(R.string.settings_screen_secondary_layouts),
|
||||||
settings = LayoutType.entries.filter { it != LayoutType.MAIN }.map { Settings.PREF_LAYOUT_PREFIX + it.name }
|
settings = LayoutType.entries.filter { it != LayoutType.MAIN }.map { Settings.PREF_LAYOUT_PREFIX + it.name }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createLayoutSettings(context: Context) = listOf(
|
fun createLayoutSettings(context: Context) = LayoutType.entries.filter { it != LayoutType.MAIN }.map { layoutType ->
|
||||||
Setting(context, Settings.PREF_LAYOUT_PREFIX + LayoutType.MAIN, R.string.customize_functional_key_layouts) { // todo: title
|
|
||||||
// todo: actual content
|
|
||||||
},
|
|
||||||
) + LayoutType.entries.filter { it != LayoutType.MAIN }.map { layoutType ->
|
|
||||||
Setting(context, Settings.PREF_LAYOUT_PREFIX + layoutType, layoutType.displayNameId) { setting ->
|
Setting(context, Settings.PREF_LAYOUT_PREFIX + layoutType, layoutType.displayNameId) { setting ->
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
val prefs = ctx.prefs()
|
val prefs = ctx.prefs()
|
||||||
|
|
|
@ -539,8 +539,12 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM
|
||||||
<string name="customize_symbols_number_layouts">Customize symbols and number layouts</string>
|
<string name="customize_symbols_number_layouts">Customize symbols and number layouts</string>
|
||||||
<!-- Title for customizing functional key layouts -->
|
<!-- Title for customizing functional key layouts -->
|
||||||
<string name="customize_functional_key_layouts">Customize functional key layouts</string>
|
<string name="customize_functional_key_layouts">Customize functional key layouts</string>
|
||||||
|
<!-- Settings screen title for secondary layouts (functional keys, symbols, numpad, ...) -->
|
||||||
|
<string name="settings_screen_secondary_layouts">Secondary layouts</string>
|
||||||
<!-- Name for functional keys layout -->
|
<!-- Name for functional keys layout -->
|
||||||
<string name="layout_functional_keys" tools:keep="@string/layout_functional_keys">Functional keys</string>
|
<string name="layout_functional_keys" tools:keep="@string/layout_functional_keys">Functional keys</string>
|
||||||
|
<!-- Name for functional keys layout for tablets -->
|
||||||
|
<string name="layout_functional_keys_tablet" tools:keep="@string/layout_functional_keys_tablet">Functional keys (large screen)</string>
|
||||||
<!-- Name for functional keys layout when showing symbols layout -->
|
<!-- Name for functional keys layout when showing symbols layout -->
|
||||||
<string name="layout_functional_keys_symbols" tools:keep="@string/layout_functional_keys_symbols">Functional keys (Symbols)</string>
|
<string name="layout_functional_keys_symbols" tools:keep="@string/layout_functional_keys_symbols">Functional keys (Symbols)</string>
|
||||||
<!-- Name for functional keys layout when showing more symbols layout -->
|
<!-- Name for functional keys layout when showing more symbols layout -->
|
||||||
|
|
13
app/src/test/java/helium314/keyboard/LayoutTest.kt
Normal file
13
app/src/test/java/helium314/keyboard/LayoutTest.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package helium314.keyboard
|
||||||
|
|
||||||
|
import helium314.keyboard.latin.utils.LayoutType
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class LayoutTest {
|
||||||
|
// todo: add more
|
||||||
|
@Test fun extraValueToMainLayout() {
|
||||||
|
val extraValue = "KeyboardLayoutSet=MAIN:qwertz+,SupportTouchPositionCorrection"
|
||||||
|
assertEquals("qwertz+", LayoutType.getMainLayoutFromExtraValue(extraValue))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue