From 6bcc162f1e3d8add5acd7b0d5fe1b44fb7f68264 Mon Sep 17 00:00:00 2001 From: Aleksandras Kostarevas Date: Thu, 19 Sep 2024 12:21:09 +0300 Subject: [PATCH] Move layout-language mapping to mapping.yaml, update AddLanguage screen --- .../layouts/Serbian/serbian_qwertz.yaml | 2 +- java/assets/layouts/Slavic/east_slavic.yaml | 2 +- java/assets/layouts/mapping.yaml | 521 ++++++++++++++++++ .../org/futo/inputmethod/latin/Subtypes.kt | 12 +- .../latin/uix/KeyboardLayoutPreview.kt | 17 +- .../latin/uix/actions/EmojiAction.kt | 20 +- .../latin/uix/settings/SettingsNavigator.kt | 11 +- .../latin/uix/settings/pages/AddLanguage.kt | 287 +++++----- .../inputmethod/v2keyboard/LayoutManager.kt | 35 +- .../inputmethod/v2keyboard/MoreKeysMapping.kt | 1 - 10 files changed, 725 insertions(+), 183 deletions(-) create mode 100644 java/assets/layouts/mapping.yaml diff --git a/java/assets/layouts/Serbian/serbian_qwertz.yaml b/java/assets/layouts/Serbian/serbian_qwertz.yaml index 5df7c90ef..9445e9bba 100644 --- a/java/assets/layouts/Serbian/serbian_qwertz.yaml +++ b/java/assets/layouts/Serbian/serbian_qwertz.yaml @@ -1,5 +1,5 @@ name: Serbian QWERTZ -languages: sr-ZZ +languages: sr-Latn rows: - letters: q w e r t z u i o p š - letters: a s d f g h j k l č ć diff --git a/java/assets/layouts/Slavic/east_slavic.yaml b/java/assets/layouts/Slavic/east_slavic.yaml index e4194414b..b28149eea 100644 --- a/java/assets/layouts/Slavic/east_slavic.yaml +++ b/java/assets/layouts/Slavic/east_slavic.yaml @@ -1,4 +1,4 @@ -name: Русский +name: East Slavic languages: ru be bg bn kk ky uk rows: - letters: й ц у к е н г ш щ з х diff --git a/java/assets/layouts/mapping.yaml b/java/assets/layouts/mapping.yaml new file mode 100644 index 000000000..7f1176dab --- /dev/null +++ b/java/assets/layouts/mapping.yaml @@ -0,0 +1,521 @@ +languages: + af: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + ar: + - arabic + - lulua + az_AZ: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + be_BY: + - east_slavic + - russian_student + - russian_yavert + - russian_yazhert + bg: + - bulgarian + - bulgarian_bds + bn_BD: + - bengali_akkhor + bn_IN: + - bengali + ca: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + ckb: + - kurdish + cs: + - qwertz + - qwerty + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + da: + - nordic + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + de: + - german + - qwertz + - swiss + - qwerty + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + de_CH: + - swiss + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + el: + - greek + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + en_GB: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + en_IN: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + en_US: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + eo: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + es: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + es_419: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + es_US: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + et_EE: + - nordic + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + eu_ES: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + fa: + - farsi + fi: + - nordic + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + fr: + - azerty + - swiss + - qwerty + - qwertz + - dvorak + - colemak + - bepo + - pcqwerty + - nordic + fr_CA: + - qwerty + - swiss + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + fr_CH: + - swiss + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + gl_ES: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + hi: + - hindi + - hindi_compact + hi_Latn: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + hr: + - qwertz + - qwerty + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + hu: + - qwertz + - qwerty + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + hy_AM: + - armenian_phonetic + in: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + is: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + it: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + it_CH: + - swiss + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + iw: + - hebrew + ka_GE: + - georgian + kk: + - east_slavic + - russian_student + - russian_yavert + - russian_yazhert + km_KH: + - khmer + kn_IN: + - kannada + ky: + - east_slavic + - russian_student + - russian_yavert + - russian_yazhert + lo_LA: + - lao + lt: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + lv: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + mk: + - south_slavic + ml_IN: + - malayalam + mn_MN: + - mongolian + mr_IN: + - marathi + ms_MY: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + nb: + - nordic + ne_NP: + - nepali_romanized + - nepali_traditional + nl: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + nl_BE: + - azerty + - qwerty + - qwertz + - dvorak + - colemak + - bepo + - pcqwerty + - nordic + pl: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + pt_BR: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + pt_PT: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + ro: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + ru: + - east_slavic + - russian_student + - russian_yavert + - russian_yazhert + si_LK: + - sinhala + sk: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + sl: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + sr: + - south_slavic + sr_Latn: + - serbian_qwertz + sv: + - nordic + sw: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + ta_IN: + - tamil + ta_LK: + - tamil + ta_SG: + - tamil + te_IN: + - telugu + th: + - thai + tl: + - spanish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + tr: + - turkish + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + uk: + - east_slavic + - russian_student + - russian_yavert + - russian_yazhert + uz_UZ: + - uzbek + vi: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + zu: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + zz: + - qwerty + - qwertz + - dvorak + - azerty + - colemak + - bepo + - pcqwerty + - nordic + ipa: + - ipa \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/Subtypes.kt b/java/src/org/futo/inputmethod/latin/Subtypes.kt index d198a485d..1cb50944e 100644 --- a/java/src/org/futo/inputmethod/latin/Subtypes.kt +++ b/java/src/org/futo/inputmethod/latin/Subtypes.kt @@ -43,10 +43,10 @@ import org.futo.inputmethod.latin.uix.setSettingBlocking import org.futo.inputmethod.latin.uix.settings.NavigationItem import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle import org.futo.inputmethod.latin.uix.settings.SettingsActivity -import org.futo.inputmethod.latin.uix.settings.pages.LocaleLayoutMap import org.futo.inputmethod.latin.uix.settings.useDataStoreValue import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils +import org.futo.inputmethod.v2keyboard.LayoutManager import java.util.Locale import kotlin.math.sign @@ -110,7 +110,7 @@ object Subtypes { var numAdded = 0 for(i in 0 until locales.size()) { val locale = locales.get(i).stripExtensionsIfNeeded() - val layout = findClosestLocaleLayouts(locale).firstOrNull() ?: continue + val layout = findClosestLocaleLayouts(context, locale).firstOrNull() ?: continue addLanguage(context, locale, layout) numAdded += 1 @@ -124,10 +124,8 @@ object Subtypes { context.setSettingBlocking(ActiveSubtype.key, context.getSettingBlocking(SubtypesSetting).firstOrNull() ?: return) } - fun findClosestLocaleLayouts(locale: Locale): List { - val supportedLocales = LocaleLayoutMap.toList().associate { - getLocale(it.first) to it.second - } + fun findClosestLocaleLayouts(context: Context, locale: Locale): List { + val supportedLocales = LayoutManager.getLayoutMapping(context) val perfectMatch = supportedLocales.keys.find { it.language == locale.language && it.country == locale.country } val languageMatch = supportedLocales.keys.find { it.language == locale.language } @@ -242,7 +240,7 @@ object Subtypes { for(i in 0 until locales.size()) { val locale = locales.get(i).stripExtensionsIfNeeded() - val layout = findClosestLocaleLayouts(locale).firstOrNull() ?: continue + val layout = findClosestLocaleLayouts(context, locale).firstOrNull() ?: continue val value = subtypeToString( InputMethodSubtypeBuilder() diff --git a/java/src/org/futo/inputmethod/latin/uix/KeyboardLayoutPreview.kt b/java/src/org/futo/inputmethod/latin/uix/KeyboardLayoutPreview.kt index 720f95d48..07fd35a7a 100644 --- a/java/src/org/futo/inputmethod/latin/uix/KeyboardLayoutPreview.kt +++ b/java/src/org/futo/inputmethod/latin/uix/KeyboardLayoutPreview.kt @@ -67,22 +67,17 @@ fun KeyboardViewCompose(keyboard: Keyboard?, width: Dp) { } @Composable -fun KeyboardLayoutPreview(id: String, width: Dp = 172.dp) { +fun KeyboardLayoutPreview(id: String, width: Dp = 172.dp, locale: Locale? = null) { val context = LocalContext.current - val locale = remember(id) { - LayoutManager.getLayout(context, id).languages.firstOrNull()?.let { - val l = Locale.forLanguageTag(it) - - println("SET LOCALE: $it to $l, ${l.language}, ${l.country}") - l + val loc = remember(id) { + locale ?: LayoutManager.getLayout(context, id).languages.firstOrNull()?.let { + Locale.forLanguageTag(it) } } - - val configuration = LocalConfiguration.current - val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + val isLandscape = false//configuration.orientation == Configuration.ORIENTATION_LANDSCAPE val widthPx: Int val heightPx: Int @@ -116,7 +111,7 @@ fun KeyboardLayoutPreview(id: String, width: Dp = 172.dp) { height = heightPx, gap = 4.0f, keyboardLayoutSet = id, - locale = locale ?: Locale.ENGLISH, + locale = loc ?: Locale.ENGLISH, editorInfo = editorInfo, numberRow = numberRow, useSplitLayout = isLandscape, diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt index 848679517..0a071b25c 100644 --- a/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt +++ b/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt @@ -130,19 +130,15 @@ private fun levenshteinDistance(lhs: CharSequence, rhs: CharSequence): Int { return cost[lhsLen] } -private fun List.search(searchTarget: String, keyFunction: (T) -> String): List { - val maxDistance = searchTarget.length * 2 / 3 +fun List.searchMultiple(searchTarget: String, maxDistance: Int = searchTarget.length * 2 / 3, limitLength: Boolean = false, keyFunction: (T) -> List): List { return this.mapNotNull { item -> - val key = keyFunction(item) - val distance = levenshteinDistance(searchTarget, key) - if (distance <= maxDistance) Pair(item, distance) else null - }.sortedBy { it.second }.map { it.first } -} - -private fun List.searchMultiple(searchTarget: String, keyFunction: (T) -> List): List { - val maxDistance = searchTarget.length * 2 / 3 - return this.mapNotNull { item -> - val keys = keyFunction(item) + val keys = keyFunction(item).let { + if(limitLength) { + it.map { it.substring(0 until searchTarget.length.coerceAtMost(it.length)) } + } else { + it + } + } val minDistanceKey = keys.minByOrNull { levenshteinDistance(searchTarget, it) } val minDistance = minDistanceKey?.let { levenshteinDistance(searchTarget, it) } if (minDistance != null && minDistance <= maxDistance) Pair(item, minDistance) else null diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsNavigator.kt b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsNavigator.kt index 87a132c51..65d286a93 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/SettingsNavigator.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/SettingsNavigator.kt @@ -12,7 +12,6 @@ import androidx.navigation.compose.rememberNavController import org.futo.inputmethod.latin.R import org.futo.inputmethod.latin.uix.ErrorDialog import org.futo.inputmethod.latin.uix.InfoDialog -import org.futo.inputmethod.latin.uix.settings.pages.AddLanguageScreen import org.futo.inputmethod.latin.uix.settings.pages.AdvancedParametersScreen import org.futo.inputmethod.latin.uix.settings.pages.BlacklistScreen import org.futo.inputmethod.latin.uix.settings.pages.CreditsScreen @@ -25,6 +24,8 @@ import org.futo.inputmethod.latin.uix.settings.pages.LanguagesScreen import org.futo.inputmethod.latin.uix.settings.pages.PaymentScreen import org.futo.inputmethod.latin.uix.settings.pages.PaymentThankYouScreen import org.futo.inputmethod.latin.uix.settings.pages.PredictiveTextScreen +import org.futo.inputmethod.latin.uix.settings.pages.SelectLanguageScreen +import org.futo.inputmethod.latin.uix.settings.pages.SelectLayoutsScreen import org.futo.inputmethod.latin.uix.settings.pages.ThemeScreen import org.futo.inputmethod.latin.uix.settings.pages.VoiceInputScreen import org.futo.inputmethod.latin.uix.settings.pages.addModelManagerNavigation @@ -53,8 +54,8 @@ fun SettingsNavigator( ) { composable("home") { HomeScreen(navController) } composable("languages") { LanguagesScreen(navController) } - composable("addLanguage") { AddLanguageScreen(navController) } - composable("addLayout/{lang}") { AddLanguageScreen(navController, it.arguments?.getString("lang")?.urlDecode()) } + composable("addLanguage") { SelectLanguageScreen(navController) } + composable("addLayout/{lang}") { SelectLayoutsScreen(navController, it.arguments?.getString("lang")?.urlDecode() ?: "") } composable("predictiveText") { PredictiveTextScreen(navController) } composable("advancedparams") { AdvancedParametersScreen(navController) } addTypingNavigation(navController) @@ -87,8 +88,4 @@ fun SettingsNavigator( } addModelManagerNavigation(navController) } - - LaunchedEffect(Unit) { - navController.navigate("devlayouts") - } } \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/AddLanguage.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/AddLanguage.kt index d6513244a..215b928c6 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/AddLanguage.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/AddLanguage.kt @@ -1,173 +1,184 @@ package org.futo.inputmethod.latin.uix.settings.pages +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.PlatformImeOptions +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import org.futo.inputmethod.latin.Subtypes -import org.futo.inputmethod.latin.stripExtensionsIfNeeded -import org.futo.inputmethod.latin.uix.settings.DropDownPicker +import org.futo.inputmethod.latin.uix.KeyboardLayoutPreview +import org.futo.inputmethod.latin.uix.actions.searchMultiple +import org.futo.inputmethod.latin.uix.settings.NavigationItem +import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle import org.futo.inputmethod.latin.uix.settings.ScreenTitle import org.futo.inputmethod.latin.uix.settings.ScrollableList -import org.futo.inputmethod.latin.uix.settings.SettingItem +import org.futo.inputmethod.latin.uix.urlEncode +import org.futo.inputmethod.v2keyboard.LayoutManager +import java.text.Normalizer +import java.util.Locale -val QwertyVariants = listOf("qwerty", "qwertz", "dvorak", "azerty", "colemak", "bepo", "pcqwerty", "nordic") - -fun makeQwertyWithPrimary(primary: String): List { - return listOf(primary) + QwertyVariants.filter { it != primary } +private val alphaRegex = Regex("[^a-zA-Z\\s]") +fun normalize(str: String): String { + return Normalizer.normalize(str, Normalizer.Form.NFD) + .replace(alphaRegex, "") } -fun makeQwertyWithPrimary(primary: String, secondary: String): List { - return listOf(primary, secondary) + QwertyVariants.filter { it != primary && it != secondary } -} - - -val LocaleLayoutMap = mapOf( - "af" to QwertyVariants, - "ar" to listOf("arabic"), - "az_AZ" to QwertyVariants, - "be_BY" to listOf("east_slavic", "east_slavic_phonetic"), - "bg" to listOf("bulgarian", "bulgarian_bds"), - "bn_BD" to listOf("bengali_akkhor"), - "bn_IN" to listOf("bengali"), - "ca" to makeQwertyWithPrimary("spanish"), - "cs" to makeQwertyWithPrimary("qwertz"), - "da" to makeQwertyWithPrimary("nordic"), - "de" to makeQwertyWithPrimary("qwertz"), - "de_CH" to makeQwertyWithPrimary("swiss"), - "el" to makeQwertyWithPrimary("greek"), - "en_IN" to QwertyVariants, - "en_US" to QwertyVariants, - "en_GB" to QwertyVariants, - "eo" to makeQwertyWithPrimary("spanish"), - "es" to makeQwertyWithPrimary("spanish"), - "es_US" to makeQwertyWithPrimary("spanish"), - "es_419" to makeQwertyWithPrimary("spanish"), - "et_EE" to makeQwertyWithPrimary("nordic"), - "eu_ES" to makeQwertyWithPrimary("spanish"), - "fa" to listOf("farsi"), - "ckb" to listOf("kurdish"), - "fi" to makeQwertyWithPrimary("nordic"), - "fr" to makeQwertyWithPrimary("azerty", "swiss"), - "fr_CA" to makeQwertyWithPrimary("qwerty", "swiss"), - "fr_CH" to makeQwertyWithPrimary("swiss"), - "gl_ES" to makeQwertyWithPrimary("spanish"), - "hi" to listOf("hindi", "hindi_compact"), - //"hi_ZZ" to QwertyVariants, - "hr" to makeQwertyWithPrimary("qwertz"), - "hu" to makeQwertyWithPrimary("qwertz"), - "hy_AM" to listOf("armenian_phonetic"), - "in" to QwertyVariants, - "is" to QwertyVariants, - "it" to QwertyVariants, - "it_CH" to makeQwertyWithPrimary("swiss"), - "iw" to listOf("hebrew"), - "ka_GE" to listOf("georgian"), - "kk" to listOf("east_slavic", "east_slavic_phonetic"), - "km_KH" to listOf("khmer"), - "kn_IN" to listOf("kannada"), - "ky" to listOf("east_slavic", "east_slavic_phonetic"), - "lo_LA" to listOf("lao"), - "lt" to QwertyVariants, - "lv" to QwertyVariants, - "mk" to listOf("south_slavic"), - "ml_IN" to listOf("malayalam"), - "mn_MN" to listOf("mongolian"), - "mr_IN" to listOf("marathi"), - "ms_MY" to QwertyVariants, - "nb" to listOf("nordic"), - "ne_NP" to listOf("nepali_romanized", "nepali_traditional"), - "nl" to QwertyVariants, - "nl_BE" to makeQwertyWithPrimary("azerty"), - "pl" to QwertyVariants, - "pt_BR" to QwertyVariants, - "pt_PT" to QwertyVariants, - "ro" to QwertyVariants, - "ru" to listOf("east_slavic", "east_slavic_phonetic"), - "si_LK" to listOf("sinhala"), - "sk" to QwertyVariants, - "sl" to QwertyVariants, - "sr" to listOf("south_slavic"), - "sr_ZZ" to listOf("serbian_qwertz"), - "sv" to listOf("nordic"), - "sw" to QwertyVariants, - "ta_IN" to listOf("tamil"), - "ta_LK" to listOf("tamil"), - "ta_SG" to listOf("tamil"), - "te_IN" to listOf("telugu"), - "th" to listOf("thai"), - "tl" to makeQwertyWithPrimary("spanish"), - "tr" to makeQwertyWithPrimary("turkish"), - "uk" to listOf("east_slavic", "east_slavic_phonetic"), - "uz_UZ" to listOf("uzbek"), - "vi" to QwertyVariants, - "zu" to QwertyVariants, - "zz" to QwertyVariants, -) @Preview @Composable -fun AddLanguageScreen(navController: NavHostController = rememberNavController(), defaultLocale: String? = null) { +fun SelectLanguageScreen(navController: NavHostController = rememberNavController()) { val context = LocalContext.current - val defaultLocaleVal = defaultLocale ?: context.resources.configuration.locale.stripExtensionsIfNeeded().toString() - val defaultLayout = Subtypes.findClosestLocaleLayouts(Subtypes.getLocale(defaultLocaleVal)).firstOrNull() ?: "qwerty" + val layoutMapping = remember { LayoutManager.getLayoutMapping(context) } - val selectedLocale: MutableState = remember { mutableStateOf(defaultLocale ?: context.resources.configuration.locale.stripExtensionsIfNeeded().toString()) } - val selectedLayout: MutableState = remember { mutableStateOf(defaultLayout) } + val systemLocale = remember { context.resources.configuration.locales[0] } + val textFieldValue = remember { mutableStateOf(TextFieldValue("")) } + + val locales = remember { + layoutMapping.keys.toList().sortedBy { + it.getDisplayName(systemLocale) + } + } + + val searchKeys = if(textFieldValue.value.text.isEmpty()) { + locales + } else { + locales.searchMultiple(textFieldValue.value.text.lowercase(), limitLength = true, maxDistance = Int.MAX_VALUE) { + listOf( + it.language, it.getDisplayName(systemLocale), it.getDisplayName(it) + ).map { normalize(it.lowercase()) }.flatMap { it.split(" ") }.filter { it.isNotBlank() } + } + } + + LazyColumn { + item { + ScreenTitle("Select language to add", showBack = true, navController) + } + + item { + Surface(color = MaterialTheme.colorScheme.surfaceContainerHighest, shape = RoundedCornerShape(8.dp), modifier = Modifier.padding(8.dp).height(48.dp).fillMaxWidth()) { + Row(modifier = Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Default.Search, contentDescription = "Search") + Spacer(modifier = Modifier.width(8.dp)) + BasicTextField( + value = textFieldValue.value, + onValueChange = { textFieldValue.value = it }, + keyboardOptions = KeyboardOptions.Default.copy( + platformImeOptions = PlatformImeOptions( + privateImeOptions = "org.futo.inputmethod.latin.NoSuggestions=1,org.futo.inputmethod.latin.ForceLayout=qwerty,org.futo.inputmethod.latin.ForceLocale=zz" + ) + ), + modifier = Modifier.weight(1.0f), + cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface), + textStyle = TextStyle.Default.copy(color = MaterialTheme.colorScheme.onSurface) + ) + + } + } + } + + items(searchKeys) { + NavigationItem( + title = it.getDisplayName(systemLocale), + subtitle = it.getDisplayName(it), + style = NavigationItemStyle.MiscNoArrow, + navigate = { + navController.navigate("addLayout/" + it.toLanguageTag().urlEncode()) + } + ) + } + } +} + + +@Composable +fun LayoutPreview(name: String, locale: Locale, onClick: () -> Unit) { + val context = LocalContext.current + val layoutName = remember { LayoutManager.getLayout(context, name).name } + + Box( + Modifier.padding(4.dp).fillMaxWidth().border( + 1.dp, + MaterialTheme.colorScheme.outlineVariant, + RoundedCornerShape(8.dp) + ).clickable { onClick() }) { + Column( + modifier = Modifier.fillMaxWidth().padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + KeyboardLayoutPreview(id = name, width = 172.dp, locale = locale) + + Text( + layoutName, + style = TextSmallStyle, + modifier = Modifier.padding(4.dp), + textAlign = TextAlign.Center + ) + } + } +} + +@Preview +@Composable +fun SelectLayoutsScreen(navController: NavHostController = rememberNavController(), language: String = "en_US") { + val context = LocalContext.current + + val locale = remember { Locale.forLanguageTag(language.replace("_", "-")) } + + val layoutMapping = remember { LayoutManager.getLayoutMapping(context) } + + val systemLocale = remember { context.resources.configuration.locales[0] } + + val relevantLayouts = remember { + layoutMapping.entries.filter { + it.key.language == locale.language + }.flatMap { it.value }.toSet() + } - val keys = remember { LocaleLayoutMap.keys.toList() } ScrollableList { - ScreenTitle("Add Language", showBack = true, navController) + ScreenTitle(locale.getDisplayName(locale), showBack = true, navController) - SettingItem(title = "Language") { - DropDownPicker( - "", - keys, - selectedLocale.value, - { - selectedLocale.value = it - selectedLayout.value = LocaleLayoutMap[it]!!.first() - }, - { - Subtypes.getNameForLocale(it) - }, - modifier = Modifier.width(180.dp) - ) - } + ScreenTitle("Select a layout to add") - SettingItem(title = "Layout") { - DropDownPicker( - "", - LocaleLayoutMap[selectedLocale.value] ?: listOf(), - selectedLayout.value, - { selectedLayout.value = it }, - { Subtypes.getLayoutName(context, it) }, - modifier = Modifier.width(180.dp) - ) - } + relevantLayouts.forEach { + LayoutPreview(it, locale) { + Subtypes.addLanguage(context, locale, it) - Button(onClick = { - Subtypes.addLanguage( - context, - Subtypes.getLocale(selectedLocale.value), - selectedLayout.value - ) - - navController.navigateUp() - }, modifier = Modifier.fillMaxWidth().padding(16.dp)) { - Text("Add") + // Go back to languages + for(x in 0 until 3) navController.navigateUp() + navController.navigate("languages") + } } } } \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/v2keyboard/LayoutManager.kt b/java/src/org/futo/inputmethod/v2keyboard/LayoutManager.kt index ff2fd9be9..2c4b085d5 100644 --- a/java/src/org/futo/inputmethod/v2keyboard/LayoutManager.kt +++ b/java/src/org/futo/inputmethod/v2keyboard/LayoutManager.kt @@ -6,13 +6,20 @@ import android.util.Log import com.charleskorn.kaml.PolymorphismStyle import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.YamlConfiguration +import kotlinx.serialization.Serializable import kotlinx.serialization.modules.EmptySerializersModule import org.futo.inputmethod.latin.uix.actions.BugInfo import org.futo.inputmethod.latin.uix.actions.BugViewerState import java.util.Locale +@Serializable +data class Mappings( + val languages: Map> +) + object LayoutManager { private var layoutsById: Map? = null + private var localeToLayoutsMappings: Map>? = null private var initialized = false private fun listFilesRecursively(assetManager: AssetManager, path: String): List { @@ -26,7 +33,7 @@ object LayoutManager { private fun getAllLayoutPaths(assetManager: AssetManager): List { return listFilesRecursively(assetManager, "layouts").filter { - it.endsWith(".yml") || it.endsWith(".yaml") + (it.endsWith(".yml") || it.endsWith(".yaml")) && it != "mapping.yaml" } } @@ -35,6 +42,10 @@ object LayoutManager { initialized = true + localeToLayoutsMappings = parseMappings(context, "layouts/mapping.yaml").languages.mapKeys { + Locale.forLanguageTag(it.key.replace("_", "-")) + } + val assetManager = context.assets val layoutPaths = getAllLayoutPaths(assetManager) @@ -65,10 +76,10 @@ object LayoutManager { return layoutsById?.get(name) ?: throw IllegalArgumentException("Failed to find keyboard layout $name. Available layouts: ${layoutsById?.keys}") } - fun queryLayoutsForLocale(locale: Locale): List { - val language = locale.language - //val script = locale.getKeyboardScript() - return layoutsById!!.values.filter { it.languages.contains(language) } + fun getLayoutMapping(context: Context): Map> { + init(context) + + return localeToLayoutsMappings!! } fun getAllLayoutNames(context: Context): List { @@ -80,6 +91,20 @@ object LayoutManager { } } +private fun parseMappings(context: Context, mappingsPath: String): Mappings { + val yaml = Yaml( + EmptySerializersModule(), + YamlConfiguration( + polymorphismStyle = PolymorphismStyle.Property, + allowAnchorsAndAliases = true + ) + ) + return context.assets.open(mappingsPath).use { inputStream -> + val yamlString = inputStream.bufferedReader().use { it.readText() } + + yaml.decodeFromString(Mappings.serializer(), yamlString) + } +} private fun parseKeyboardYaml(context: Context, layoutPath: String): Keyboard { val yaml = Yaml( diff --git a/java/src/org/futo/inputmethod/v2keyboard/MoreKeysMapping.kt b/java/src/org/futo/inputmethod/v2keyboard/MoreKeysMapping.kt index b65725e38..98cb07a0c 100644 --- a/java/src/org/futo/inputmethod/v2keyboard/MoreKeysMapping.kt +++ b/java/src/org/futo/inputmethod/v2keyboard/MoreKeysMapping.kt @@ -1,6 +1,5 @@ package org.futo.inputmethod.v2keyboard -import org.futo.inputmethod.keyboard.internal.KeyboardLayoutKind import org.futo.inputmethod.latin.common.Constants fun getDefaultMoreKeysForKey(code: Int, relevantSpecShortcut: List?): String {