adjust arrangement for layouts

so we can have more than one layout per type (was possible only for main)
This commit is contained in:
Helium314 2025-02-14 13:37:21 +01:00
parent 846388de97
commit 5ba40f7875
98 changed files with 260 additions and 207 deletions

View file

@ -13,8 +13,8 @@ android {
applicationId = "helium314.keyboard" applicationId = "helium314.keyboard"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 2303 versionCode = 2304
versionName = "2.3+dev2" versionName = "2.3+dev3"
ndk { ndk {
abiFilters.clear() abiFilters.clear()
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")) abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))

View file

@ -16,9 +16,9 @@ import helium314.keyboard.keyboard.internal.KeyboardBuilder;
import helium314.keyboard.keyboard.internal.KeyboardIconsSet; import helium314.keyboard.keyboard.internal.KeyboardIconsSet;
import helium314.keyboard.keyboard.internal.KeyboardParams; import helium314.keyboard.keyboard.internal.KeyboardParams;
import helium314.keyboard.keyboard.internal.UniqueKeysCache; import helium314.keyboard.keyboard.internal.UniqueKeysCache;
import helium314.keyboard.keyboard.internal.keyboard_parser.LayoutParser;
import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfos; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfos;
import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfosKt; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfosKt;
import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser;
import helium314.keyboard.latin.RichInputMethodManager; import helium314.keyboard.latin.RichInputMethodManager;
import helium314.keyboard.latin.RichInputMethodSubtype; import helium314.keyboard.latin.RichInputMethodSubtype;
import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.settings.Settings;
@ -104,7 +104,7 @@ public final class KeyboardLayoutSet {
private static void clearKeyboardCache() { private static void clearKeyboardCache() {
sKeyboardCache.clear(); sKeyboardCache.clear();
sUniqueKeysCache.clear(); sUniqueKeysCache.clear();
RawKeyboardParser.INSTANCE.clearCache(); LayoutParser.INSTANCE.clearCache();
KeyboardIconsSet.Companion.setNeedsReload(true); KeyboardIconsSet.Companion.setNeedsReload(true);
} }

View file

@ -17,10 +17,9 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData
import helium314.keyboard.latin.common.isEmoji import helium314.keyboard.latin.common.isEmoji
import helium314.keyboard.latin.define.DebugFlags import helium314.keyboard.latin.define.DebugFlags
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.LayoutType
import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT
import helium314.keyboard.latin.utils.POPUP_KEYS_NUMBER import helium314.keyboard.latin.utils.POPUP_KEYS_NUMBER
import helium314.keyboard.latin.utils.ScriptUtils
import helium314.keyboard.latin.utils.ScriptUtils.script
import helium314.keyboard.latin.utils.replaceFirst import helium314.keyboard.latin.utils.replaceFirst
import helium314.keyboard.latin.utils.splitAt import helium314.keyboard.latin.utils.splitAt
import helium314.keyboard.latin.utils.sumOf import helium314.keyboard.latin.utils.sumOf
@ -47,7 +46,20 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
fun parseLayout(): ArrayList<ArrayList<KeyParams>> { fun parseLayout(): ArrayList<ArrayList<KeyParams>> {
params.readAttributes(context, null) params.readAttributes(context, null)
val baseKeys = RawKeyboardParser.parseLayout(params, context) // todo: maybe determine layoutType earlier, and to less stuff based on elementId
val layoutType = when (params.mId.mElementId) {
KeyboardId.ELEMENT_SYMBOLS -> LayoutType.SYMBOLS
KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> LayoutType.MORE_SYMBOLS
KeyboardId.ELEMENT_PHONE -> LayoutType.PHONE
KeyboardId.ELEMENT_PHONE_SYMBOLS -> LayoutType.PHONE_SYMBOLS
KeyboardId.ELEMENT_NUMBER -> LayoutType.NUMBER
KeyboardId.ELEMENT_NUMPAD -> if (Settings.getInstance().current.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE)
LayoutType.NUMPAD_LANDSCAPE else LayoutType.NUMPAD
KeyboardId.ELEMENT_EMOJI_BOTTOM_ROW -> LayoutType.EMOJI_BOTTOM
KeyboardId.ELEMENT_CLIPBOARD_BOTTOM_ROW -> LayoutType.CLIPBOARD_BOTTOM
else -> LayoutType.MAIN
}
val baseKeys = LayoutParser.parseLayout(layoutType, params, context)
val keysInRows = createRows(baseKeys) val keysInRows = createRows(baseKeys)
val heightRescale: Float val heightRescale: Float
if (params.mId.isEmojiClipBottomRow) { if (params.mId.isEmojiClipBottomRow) {
@ -94,7 +106,7 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
if (!params.mAllowRedundantPopupKeys) if (!params.mAllowRedundantPopupKeys)
params.baseKeys = baseKeys.flatMap { it.map { it.toKeyParams(params) } } params.baseKeys = baseKeys.flatMap { it.map { it.toKeyParams(params) } }
val allFunctionalKeys = RawKeyboardParser.parseLayout(params, context, true) val allFunctionalKeys = LayoutParser.parseLayout(LayoutType.FUNCTIONAL, params, context)
adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys) adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys)
if (allFunctionalKeys.none { it.singleOrNull()?.isKeyPlaceholder() == true }) if (allFunctionalKeys.none { it.singleOrNull()?.isKeyPlaceholder() == true })
@ -272,8 +284,7 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
} }
private fun addSymbolPopupKeys(baseKeys: MutableList<MutableList<KeyData>>) { private fun addSymbolPopupKeys(baseKeys: MutableList<MutableList<KeyData>>) {
val layoutName = if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS val layout = LayoutParser.parseLayout(LayoutType.SYMBOLS, params, context)
val layout = RawKeyboardParser.parseLayout(layoutName, params, context)
layout.forEachIndexed { i, row -> layout.forEachIndexed { i, row ->
val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed
row.forEachIndexed { j, key -> row.forEachIndexed { j, key ->
@ -283,7 +294,7 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
} }
private fun getNumberRow(): MutableList<KeyData> { private fun getNumberRow(): MutableList<KeyData> {
val row = RawKeyboardParser.parseLayout(LAYOUT_NUMBER_ROW, params, context).first() val row = LayoutParser.parseLayout(LayoutType.NUMBER_ROW, params, context).first()
val localizedNumbers = params.mLocaleKeyboardInfos.localizedNumberKeys val localizedNumbers = params.mLocaleKeyboardInfos.localizedNumberKeys
if (localizedNumbers?.size != 10) return row if (localizedNumbers?.size != 10) return row
if (Settings.getInstance().current.mLocalizedNumberRow) { if (Settings.getInstance().current.mLocalizedNumberRow) {

View file

@ -2,8 +2,6 @@
package helium314.keyboard.keyboard.internal.keyboard_parser package helium314.keyboard.keyboard.internal.keyboard_parser
import android.content.Context import android.content.Context
import android.content.res.Configuration
import helium314.keyboard.keyboard.KeyboardId
import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AbstractKeyData import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AbstractKeyData
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyData import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyData
@ -19,45 +17,32 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey
import helium314.keyboard.latin.common.splitOnWhitespace import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.Defaults.default
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
import helium314.keyboard.latin.utils.LayoutType
import helium314.keyboard.latin.utils.LayoutType.Companion.folder
import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.ScriptUtils
import helium314.keyboard.latin.utils.ScriptUtils.script
import helium314.keyboard.latin.utils.getCustomFunctionalLayoutName
import helium314.keyboard.latin.utils.getCustomLayoutFile
import helium314.keyboard.latin.utils.getCustomLayoutFiles import helium314.keyboard.latin.utils.getCustomLayoutFiles
import helium314.keyboard.latin.utils.prefs
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.polymorphic
import java.io.File
object RawKeyboardParser { object LayoutParser {
private const val TAG = "RawKeyboardParser" private const val TAG = "LayoutParser"
private val rawLayoutCache = hashMapOf<String, (KeyboardParams) -> MutableList<MutableList<KeyData>>>() private val layoutCache = hashMapOf<String, (KeyboardParams) -> MutableList<MutableList<KeyData>>>()
val symbolAndNumberLayouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC, fun clearCache() = layoutCache.clear()
LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS,
LAYOUT_NUMBER_ROW, LAYOUT_EMOJI_BOTTOM_ROW, LAYOUT_CLIPBOARD_BOTTOM_ROW)
fun clearCache() = rawLayoutCache.clear() fun parseLayout(layoutType: LayoutType, params: KeyboardParams, context: Context): MutableList<MutableList<KeyData>> {
if (layoutType == LayoutType.FUNCTIONAL && !params.mId.isAlphaOrSymbolKeyboard)
fun parseLayout(params: KeyboardParams, context: Context, isFunctional: Boolean = false): MutableList<MutableList<KeyData>> { return mutableListOf(mutableListOf()) // no functional keys
val layoutName = if (isFunctional) { val layoutName = if (layoutType == LayoutType.MAIN) params.mId.mSubtype.mainLayoutName
if (!params.mId.isAlphaOrSymbolKeyboard) return mutableListOf(mutableListOf()) else params.mId.mSubtype.layouts[layoutType] ?: Settings.getLayoutName(layoutType, context.prefs())
else getFunctionalLayoutName(params, context) return layoutCache.getOrPut(layoutType.name + layoutName) {
} else { createCacheLambda(layoutType, layoutName, context)
getLayoutName(params, context)
}
return rawLayoutCache.getOrPut(layoutName) {
createCacheLambda(layoutName, context)
}(params)
}
fun parseLayout(layoutName: String, params: KeyboardParams, context: Context): MutableList<MutableList<KeyData>> {
return rawLayoutCache.getOrPut(layoutName) {
createCacheLambda(layoutName, context)
}(params) }(params)
} }
@ -89,25 +74,12 @@ object RawKeyboardParser {
else split.first().toTextKey(split.drop(1)) else split.first().toTextKey(split.drop(1))
} }
private fun createCacheLambda(layoutName: String, context: Context): (KeyboardParams) -> MutableList<MutableList<KeyData>> { private fun createCacheLambda(layoutType: LayoutType, layoutName: String, context: Context):
val layoutFileName = getLayoutFileName(layoutName, context) (KeyboardParams) -> MutableList<MutableList<KeyData>> {
val layoutText = if (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) { val layoutFileContent = getLayoutFileContent(layoutType, layoutName.substringBefore("+"), context).trimStart()
if (layoutFileContent.startsWith("[") || (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX) && layoutFileContent.startsWith("/"))) {
try { try {
getCustomLayoutFile(layoutFileName, context).readText().trimStart() val florisKeyData = parseJsonString(layoutFileContent, false)
} catch (e: Exception) { // fall back to defaults if for some reason file is broken
val name = when {
layoutName.contains("functional") -> "functional_keys.json"
layoutName.contains("number_row") -> "number_row.txt"
layoutName.contains("symbols") -> "symbols.txt"
else -> "qwerty.txt"
}
Log.e(TAG, "cannot open layout $layoutName, falling back to $name", e)
context.assets.open("layouts${File.separator}$name").reader().use { it.readText() }
}
} else context.assets.open("layouts${File.separator}$layoutFileName").reader().use { it.readText() }
if (layoutFileName.endsWith(".json") || (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX) && layoutText.startsWith("["))) {
try {
val florisKeyData = parseJsonString(layoutText, false)
return { params -> return { params ->
florisKeyData.mapTo(mutableListOf()) { row -> florisKeyData.mapTo(mutableListOf()) { row ->
row.mapNotNullTo(mutableListOf()) { it.compute(params) } row.mapNotNullTo(mutableListOf()) { it.compute(params) }
@ -118,60 +90,26 @@ object RawKeyboardParser {
} }
} }
// not a json, or invalid json // not a json, or invalid json
val simpleKeyData = parseSimpleString(layoutText) val simpleKeyData = parseSimpleString(layoutFileContent)
return { params -> return { params ->
simpleKeyData.mapIndexedTo(mutableListOf()) { i, row -> simpleKeyData.mapIndexedTo(mutableListOf()) { i, row ->
val newRow = row.toMutableList() val newRow = row.toMutableList()
if (params.mId.isAlphabetKeyboard if (params.mId.isAlphabetKeyboard && layoutName.endsWith("+"))
&& params.mId.mSubtype.mainLayoutName.endsWith("+")
&& "$layoutName+" == params.mId.mSubtype.mainLayoutName
) {
params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) } params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) }
}
newRow newRow
} }
} }
} }
private fun getLayoutName(params: KeyboardParams, context: Context) = when (params.mId.mElementId) { private fun getLayoutFileContent(layoutType: LayoutType, layoutName: String, context: Context): String {
KeyboardId.ELEMENT_SYMBOLS -> if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX))
KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> LAYOUT_SYMBOLS_SHIFTED getCustomLayoutFiles(layoutType, context)
KeyboardId.ELEMENT_NUMPAD -> if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) .firstOrNull { it.name.startsWith(layoutName) }?.let { return it.readText() }
LAYOUT_NUMPAD_LANDSCAPE val layouts = context.assets.list(layoutType.folder)!!
else layouts.firstOrNull { it.startsWith("$layoutName.") }
LAYOUT_NUMPAD ?.let { return context.assets.open(layoutType.folder + it).reader().readText() }
KeyboardId.ELEMENT_NUMBER -> LAYOUT_NUMBER val fallback = layouts.first { it.startsWith(layoutType.default) } // must exist!
KeyboardId.ELEMENT_PHONE -> LAYOUT_PHONE return context.assets.open(layoutType.folder + fallback).reader().readText()
KeyboardId.ELEMENT_PHONE_SYMBOLS -> LAYOUT_PHONE_SYMBOLS
KeyboardId.ELEMENT_EMOJI_BOTTOM_ROW -> LAYOUT_EMOJI_BOTTOM_ROW
KeyboardId.ELEMENT_CLIPBOARD_BOTTOM_ROW -> LAYOUT_CLIPBOARD_BOTTOM_ROW
else -> params.mId.mSubtype.mainLayoutName.substringBeforeLast("+")
}
private fun getFunctionalLayoutName(params: KeyboardParams, context: Context): String {
if (Settings.getInstance().current.mHasCustomFunctionalLayout) {
getCustomFunctionalLayoutName(params.mId.mElementId, params.mId.mSubtype.rawSubtype, context)
?.let { return it }
}
return if (Settings.getInstance().isTablet) "functional_keys_tablet" else "functional_keys"
}
/** returns the file name matching the layout name, making sure the file exists (falling back to qwerty.txt) */
private fun getLayoutFileName(layoutName: String, context: Context): String {
val customFiles = getCustomLayoutFiles(context).map { it.name }
if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
return customFiles.firstOrNull { it.startsWith(layoutName)}
?: if (layoutName.contains("functional")) "functional_keys.json" else "qwerty.txt" // fallback to defaults
}
val assetsFiles by lazy { context.assets.list("layouts")!! }
return if (layoutName in symbolAndNumberLayouts) {
customFiles.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")}
?: assetsFiles.first { it.startsWith(layoutName) }
} else {
// can't be custom layout, so it must be in assets
val searchName = layoutName.substringBeforeLast("+") // consider there are layouts ending in "+" for adding extra keys
assetsFiles.firstOrNull { it.startsWith(searchName) } ?: "qwerty.txt" // in case it was removed
}
} }
// allow commenting lines by starting them with "//" // allow commenting lines by starting them with "//"

View file

@ -195,6 +195,9 @@ fun addLocaleKeyTextsToParams(context: Context, params: KeyboardParams, popupKey
} }
} }
fun hasLocalizedNumberRow(locale: Locale, context: Context) =
getStreamForLocale(locale, context)?.bufferedReader()?.readLines()?.any { it == "[number_row]" } == true
private fun createLocaleKeyTexts(context: Context, params: KeyboardParams, popupKeysSetting: Int): LocaleKeyboardInfos { private fun createLocaleKeyTexts(context: Context, params: KeyboardParams, popupKeysSetting: Int): LocaleKeyboardInfos {
val lkt = LocaleKeyboardInfos(getStreamForLocale(params.mId.locale, context), params.mId.locale) val lkt = LocaleKeyboardInfos(getStreamForLocale(params.mId.locale, context), params.mId.locale)
params.mSecondaryLocales.forEach { locale -> params.mSecondaryLocales.forEach { locale ->

View file

@ -3,7 +3,6 @@ package helium314.keyboard.latin
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import helium314.keyboard.keyboard.ColorSetting import helium314.keyboard.keyboard.ColorSetting
import helium314.keyboard.keyboard.KeyboardTheme import helium314.keyboard.keyboard.KeyboardTheme
@ -15,9 +14,13 @@ import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX
import helium314.keyboard.latin.settings.colorPrefsAndResIds import helium314.keyboard.latin.settings.colorPrefsAndResIds
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
import helium314.keyboard.latin.utils.DeviceProtectedUtils
import helium314.keyboard.latin.utils.DictionaryInfoUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils
import helium314.keyboard.latin.utils.LayoutType
import helium314.keyboard.latin.utils.LayoutType.Companion.folder
import helium314.keyboard.latin.utils.ToolbarKey import helium314.keyboard.latin.utils.ToolbarKey
import helium314.keyboard.latin.utils.defaultPinnedToolbarPref import helium314.keyboard.latin.utils.defaultPinnedToolbarPref
import helium314.keyboard.latin.utils.encodeBase36
import helium314.keyboard.latin.utils.getCustomLayoutFile import helium314.keyboard.latin.utils.getCustomLayoutFile
import helium314.keyboard.latin.utils.getCustomLayoutFiles import helium314.keyboard.latin.utils.getCustomLayoutFiles
import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged
@ -236,6 +239,120 @@ fun checkVersionUpgrade(context: Context) {
} } } }
writeCustomKeyCodes(prefs, combined) writeCustomKeyCodes(prefs, combined)
} }
if (oldVersion <= 2303) {
File(DeviceProtectedUtils.getFilesDir(context), "layouts").listFiles()?.forEach { file ->
val folder = DeviceProtectedUtils.getFilesDir(context)
when (file.name) {
"${CUSTOM_LAYOUT_PREFIX}symbols." -> {
val dir = File(folder, LayoutType.SYMBOLS.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("symbols")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.SYMBOLS.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}symbols_shifted." -> {
val dir = File(folder, LayoutType.MORE_SYMBOLS.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("symbols_shifted")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.MORE_SYMBOLS.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}symbols_arabic." -> {
val dir = File(folder, LayoutType.SYMBOLS.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("symbols_arabic")}."
file.renameTo(File(dir, name))
}
"${CUSTOM_LAYOUT_PREFIX}numpad." -> {
val dir = File(folder, LayoutType.NUMPAD.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("numpad")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.NUMPAD.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}numpad_landscape." -> {
val dir = File(folder, LayoutType.NUMPAD_LANDSCAPE.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("numpad_landscape")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.NUMPAD_LANDSCAPE.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}number." -> {
val dir = File(folder, LayoutType.NUMBER.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("number")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.NUMBER.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}phone." -> {
val dir = File(folder, LayoutType.PHONE.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("phone")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.PHONE.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}phone_symbols." -> {
val dir = File(folder, LayoutType.PHONE_SYMBOLS.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("phone_symbols")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.PHONE_SYMBOLS.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}number_row." -> {
val dir = File(folder, LayoutType.NUMBER_ROW.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("number_row")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.NUMBER_ROW.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}emoji_bottom_row." -> {
val dir = File(folder, LayoutType.EMOJI_BOTTOM.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("emoji_bottom_row")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.EMOJI_BOTTOM.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}clip_bottom_row." -> {
val dir = File(folder, LayoutType.CLIPBOARD_BOTTOM.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("clip_bottom_row")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.CLIPBOARD_BOTTOM.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}functional_keys." -> {
val dir = File(folder, LayoutType.FUNCTIONAL.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("functional_keys")}."
file.renameTo(File(dir, name))
prefs.edit().putString(Settings.PREF_LAYOUT_PREFIX + LayoutType.FUNCTIONAL.name, name).apply()
}
"${CUSTOM_LAYOUT_PREFIX}functional_keys_symbols." -> {
val dir = File(folder, LayoutType.FUNCTIONAL.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("functional_keys_symbols")}."
file.renameTo(File(dir, name))
}
"${CUSTOM_LAYOUT_PREFIX}functional_keys_symbols_shifted." -> {
val dir = File(folder, LayoutType.FUNCTIONAL.folder)
dir.mkdirs()
val name = "$CUSTOM_LAYOUT_PREFIX${encodeBase36("functional_keys_symbols_shifted")}."
file.renameTo(File(dir, name))
}
else -> {
// main layouts
val dir = File(folder, LayoutType.MAIN.folder)
dir.mkdirs()
file.renameTo(File(dir, file.name))
// todo: maybe rename to custom.latn.name. instead of custom.en-GB.name. for latin script?
// just make sure the subtypes are still working when the file name is different (need to upgrade PREF_ADDITIONAL_SUBTYPES)
// also consider name collision when a user has layout with the same name for 2 languages
// decode name, append number, encode name
}
}
}
if (prefs.contains(Settings.PREF_ADDITIONAL_SUBTYPES))
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, "")!!.replace(":", "§")).apply()
}
upgradeToolbarPrefs(prefs) upgradeToolbarPrefs(prefs)
onCustomLayoutFileListChanged() // just to be sure onCustomLayoutFileListChanged() // just to be sure
prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) } prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) }

View file

@ -35,6 +35,7 @@ class RichInputMethodSubtype private constructor(val rawSubtype: InputMethodSubt
val mainLayoutName: String get() = layouts[LayoutType.MAIN] ?: "qwerty" val mainLayoutName: String get() = layouts[LayoutType.MAIN] ?: "qwerty"
/** layout names for this subtype by LayoutType */
val layouts = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "") val layouts = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
val isCustom: Boolean get() = mainLayoutName.startsWith(CUSTOM_LAYOUT_PREFIX) val isCustom: Boolean get() = mainLayoutName.startsWith(CUSTOM_LAYOUT_PREFIX)

View file

@ -22,7 +22,6 @@ import androidx.core.content.edit
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import helium314.keyboard.dictionarypack.DictionaryPackConstants import helium314.keyboard.dictionarypack.DictionaryPackConstants
import helium314.keyboard.keyboard.KeyboardActionListener import helium314.keyboard.keyboard.KeyboardActionListener
@ -37,7 +36,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_PHONE_SYMBOLS
import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS
import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_ARABIC import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_ARABIC
import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_SHIFTED import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_SHIFTED
import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser
import helium314.keyboard.latin.AudioAndHapticFeedbackManager import helium314.keyboard.latin.AudioAndHapticFeedbackManager
import helium314.keyboard.latin.BuildConfig import helium314.keyboard.latin.BuildConfig
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
@ -165,7 +163,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
} }
private fun showCustomizeSymbolNumberLayoutsDialog() { private fun showCustomizeSymbolNumberLayoutsDialog() {
val layoutNames = RawKeyboardParser.symbolAndNumberLayouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray() /* val layoutNames = RawKeyboardParser.symbolAndNumberLayouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray()
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
.setTitle(R.string.customize_symbols_number_layouts) .setTitle(R.string.customize_symbols_number_layouts)
.setItems(layoutNames) { di, i -> .setItems(layoutNames) { di, i ->
@ -174,7 +172,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
} */ }
private fun customizeSymbolNumberLayout(layoutName: String) { private fun customizeSymbolNumberLayout(layoutName: String) {
val customLayoutName = getCustomLayoutFiles(requireContext()).map { it.name } val customLayoutName = getCustomLayoutFiles(requireContext()).map { it.name }

View file

@ -9,6 +9,7 @@ import helium314.keyboard.keyboard.KeyboardTheme
import helium314.keyboard.latin.BuildConfig import helium314.keyboard.latin.BuildConfig
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils import helium314.keyboard.latin.utils.AdditionalSubtypeUtils
import helium314.keyboard.latin.utils.JniUtils import helium314.keyboard.latin.utils.JniUtils
import helium314.keyboard.latin.utils.LayoutType
import helium314.keyboard.latin.utils.POPUP_KEYS_LABEL_DEFAULT import helium314.keyboard.latin.utils.POPUP_KEYS_LABEL_DEFAULT
import helium314.keyboard.latin.utils.POPUP_KEYS_ORDER_DEFAULT import helium314.keyboard.latin.utils.POPUP_KEYS_ORDER_DEFAULT
import helium314.keyboard.latin.utils.defaultClipboardToolbarPref import helium314.keyboard.latin.utils.defaultClipboardToolbarPref
@ -27,6 +28,22 @@ object Defaults {
PREF_POPUP_ON = dm.widthPixels >= px600 || dm.heightPixels >= px600 PREF_POPUP_ON = dm.widthPixels >= px600 || dm.heightPixels >= px600
} }
// must correspond to a file name
val LayoutType.default get() = when (this) {
LayoutType.MAIN -> "qwerty"
LayoutType.SYMBOLS -> "symbols"
LayoutType.MORE_SYMBOLS -> "symbols_shifted"
LayoutType.FUNCTIONAL -> if (Settings.getInstance().isTablet) "functional_keys_tablet" else "functional_keys"
LayoutType.NUMBER -> "number"
LayoutType.NUMBER_ROW -> "number_row"
LayoutType.NUMPAD -> "numpad"
LayoutType.NUMPAD_LANDSCAPE -> "numpad_landscape"
LayoutType.PHONE -> "phone"
LayoutType.PHONE_SYMBOLS -> "phone_symbols"
LayoutType.EMOJI_BOTTOM -> "emoji_bottom_row"
LayoutType.CLIPBOARD_BOTTOM -> "clipboard_bottom_row"
}
const val PREF_THEME_STYLE = KeyboardTheme.STYLE_MATERIAL const val PREF_THEME_STYLE = KeyboardTheme.STYLE_MATERIAL
const val PREF_ICON_STYLE = KeyboardTheme.STYLE_MATERIAL const val PREF_ICON_STYLE = KeyboardTheme.STYLE_MATERIAL
const val PREF_THEME_COLORS = KeyboardTheme.THEME_LIGHT const val PREF_THEME_COLORS = KeyboardTheme.THEME_LIGHT
@ -56,8 +73,10 @@ object Defaults {
const val PREF_LANGUAGE_SWITCH_KEY = "internal" const val PREF_LANGUAGE_SWITCH_KEY = "internal"
const val PREF_SHOW_EMOJI_KEY = false const val PREF_SHOW_EMOJI_KEY = false
const val PREF_VARIABLE_TOOLBAR_DIRECTION = true const val PREF_VARIABLE_TOOLBAR_DIRECTION = true
const val PREF_ADDITIONAL_SUBTYPES = "de:qwerty:AsciiCapable${AdditionalSubtypeUtils.PREF_SUBTYPE_SEPARATOR}" + private const val ls = AdditionalSubtypeUtils.LOCALE_AND_EXTRA_SEPARATOR
"fr:qwertz:AsciiCapable${AdditionalSubtypeUtils.PREF_SUBTYPE_SEPARATOR}hu:qwerty:AsciiCapable" private const val subs = AdditionalSubtypeUtils.PREF_SUBTYPE_SEPARATOR
const val PREF_ADDITIONAL_SUBTYPES = "de${ls}qwerty${ls}AsciiCapable${subs}" +
"fr${ls}qwertz:${ls}AsciiCapable${subs}hu${ls}qwerty${ls}AsciiCapable"
const val PREF_ENABLE_SPLIT_KEYBOARD = false const val PREF_ENABLE_SPLIT_KEYBOARD = false
const val PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = false const val PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = false
const val PREF_SPLIT_SPACER_SCALE = SettingsValues.DEFAULT_SIZE_SCALE const val PREF_SPLIT_SPACER_SCALE = SettingsValues.DEFAULT_SIZE_SCALE

View file

@ -137,7 +137,7 @@ class LanguageSettingsDialog(
val layouts = mutableListOf<String>() val layouts = mutableListOf<String>()
val displayNames = mutableListOf<String>() val displayNames = mutableListOf<String>()
infos.forEach { infos.forEach {
val mainLayoutName = it.subtype.mainLayoutName() val mainLayoutName = it.subtype.mainLayoutName() ?: "qwerty"
if (!mainLayoutName.startsWith(CUSTOM_LAYOUT_PREFIX) // don't allow copying custom layout (at least for now) if (!mainLayoutName.startsWith(CUSTOM_LAYOUT_PREFIX) // don't allow copying custom layout (at least for now)
&& !mainLayoutName.endsWith("+")) { // don't allow copying layouts only defined via extra keys && !mainLayoutName.endsWith("+")) { // don't allow copying layouts only defined via extra keys
layouts.add(mainLayoutName) layouts.add(mainLayoutName)
@ -168,7 +168,7 @@ class LanguageSettingsDialog(
private fun addSubtypeToView(subtype: SubtypeInfo) { private fun addSubtypeToView(subtype: SubtypeInfo) {
val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView) val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView)
val layoutSetName = subtype.subtype.mainLayoutName() val layoutSetName = subtype.subtype.mainLayoutName() ?: "qwerty"
row.findViewById<TextView>(R.id.language_name).text = row.findViewById<TextView>(R.id.language_name).text =
SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype.subtype) SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype.subtype)
?: subtype.subtype.displayName(context) ?: subtype.subtype.displayName(context)

View file

@ -32,6 +32,7 @@ import helium314.keyboard.latin.common.LocaleUtils;
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils; import helium314.keyboard.latin.utils.AdditionalSubtypeUtils;
import helium314.keyboard.latin.utils.DeviceProtectedUtils; import helium314.keyboard.latin.utils.DeviceProtectedUtils;
import helium314.keyboard.latin.utils.KtxKt; import helium314.keyboard.latin.utils.KtxKt;
import helium314.keyboard.latin.utils.LayoutType;
import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.Log;
import helium314.keyboard.latin.utils.ResourceUtils; import helium314.keyboard.latin.utils.ResourceUtils;
import helium314.keyboard.latin.utils.RunInLocaleKt; import helium314.keyboard.latin.utils.RunInLocaleKt;
@ -68,6 +69,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_CUSTOM_ICON_NAMES = "custom_icon_names"; public static final String PREF_CUSTOM_ICON_NAMES = "custom_icon_names";
public static final String PREF_TOOLBAR_CUSTOM_KEY_CODES = "toolbar_custom_key_codes"; public static final String PREF_TOOLBAR_CUSTOM_KEY_CODES = "toolbar_custom_key_codes";
public static final String PREF_LAYOUT_PREFIX = "layout_";
public static final String PREF_AUTO_CAP = "auto_cap"; public static final String PREF_AUTO_CAP = "auto_cap";
public static final String PREF_VIBRATE_ON = "vibrate_on"; public static final String PREF_VIBRATE_ON = "vibrate_on";
@ -549,6 +551,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return new File(DeviceProtectedUtils.getFilesDir(context), "custom_font"); return new File(DeviceProtectedUtils.getFilesDir(context), "custom_font");
} }
public static String getLayoutName(final LayoutType type, final SharedPreferences prefs) {
return prefs.getString(PREF_LAYOUT_PREFIX + type.name(), Defaults.INSTANCE.getDefault(type));
}
@Nullable @Nullable
public Typeface getCustomTypeface() { public Typeface getCustomTypeface() {
if (!sCustomTypefaceLoaded) { if (!sCustomTypefaceLoaded) {

View file

@ -144,7 +144,6 @@ public class SettingsValues {
public final SettingsValuesForSuggestion mSettingsValuesForSuggestion; public final SettingsValuesForSuggestion mSettingsValuesForSuggestion;
public final boolean mIncognitoModeEnabled; public final boolean mIncognitoModeEnabled;
public final boolean mLongPressSymbolsForNumpad; public final boolean mLongPressSymbolsForNumpad;
public final boolean mHasCustomFunctionalLayout;
public final int mEmojiMaxSdk; public final int mEmojiMaxSdk;
// User-defined colors // User-defined colors
@ -276,7 +275,6 @@ public class SettingsValues {
mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, Defaults.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD); mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, Defaults.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD);
mAutoShowToolbar = prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, Defaults.PREF_AUTO_SHOW_TOOLBAR); mAutoShowToolbar = prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, Defaults.PREF_AUTO_SHOW_TOOLBAR);
mAutoHideToolbar = suggestionsEnabled && prefs.getBoolean(Settings.PREF_AUTO_HIDE_TOOLBAR, Defaults.PREF_AUTO_HIDE_TOOLBAR); mAutoHideToolbar = suggestionsEnabled && prefs.getBoolean(Settings.PREF_AUTO_HIDE_TOOLBAR, Defaults.PREF_AUTO_HIDE_TOOLBAR);
mHasCustomFunctionalLayout = CustomLayoutUtilsKt.hasCustomFunctionalLayout(selectedSubtype, context);
mAlphaAfterEmojiInEmojiView = prefs.getBoolean(Settings.PREF_ABC_AFTER_EMOJI, Defaults.PREF_ABC_AFTER_EMOJI); mAlphaAfterEmojiInEmojiView = prefs.getBoolean(Settings.PREF_ABC_AFTER_EMOJI, Defaults.PREF_ABC_AFTER_EMOJI);
mAlphaAfterClipHistoryEntry = prefs.getBoolean(Settings.PREF_ABC_AFTER_CLIP, Defaults.PREF_ABC_AFTER_CLIP); mAlphaAfterClipHistoryEntry = prefs.getBoolean(Settings.PREF_ABC_AFTER_CLIP, Defaults.PREF_ABC_AFTER_CLIP);
mAlphaAfterSymbolAndSpace = prefs.getBoolean(Settings.PREF_ABC_AFTER_SYMBOL_SPACE, Defaults.PREF_ABC_AFTER_SYMBOL_SPACE); mAlphaAfterSymbolAndSpace = prefs.getBoolean(Settings.PREF_ABC_AFTER_SYMBOL_SPACE, Defaults.PREF_ABC_AFTER_SYMBOL_SPACE);

View file

@ -38,7 +38,7 @@ public final class AdditionalSubtypeUtils {
return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE); return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE);
} }
private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":"; public static final String LOCALE_AND_EXTRA_SEPARATOR = "§";
private static final int INDEX_OF_LANGUAGE_TAG = 0; private static final int INDEX_OF_LANGUAGE_TAG = 0;
private static final int INDEX_OF_KEYBOARD_LAYOUT = 1; private static final int INDEX_OF_KEYBOARD_LAYOUT = 1;
private static final int INDEX_OF_EXTRA_VALUE = 2; private static final int INDEX_OF_EXTRA_VALUE = 2;
@ -85,10 +85,12 @@ public final class AdditionalSubtypeUtils {
final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists( final String extraValue = StringUtils.removeFromCommaSplittableTextIfExists(
layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists( layoutExtraValue, StringUtils.removeFromCommaSplittableTextIfExists(
IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue())); IS_ADDITIONAL_SUBTYPE, subtype.getExtraValue()));
final String basePrefSubtype = SubtypeUtilsKt.locale(subtype).toLanguageTag() + LOCALE_AND_LAYOUT_SEPARATOR if (extraValue.contains(PREF_SUBTYPE_SEPARATOR) || extraValue.contains(LOCALE_AND_EXTRA_SEPARATOR))
throw new IllegalArgumentException("extra value contains not allowed characters " + extraValue);
final String basePrefSubtype = SubtypeUtilsKt.locale(subtype).toLanguageTag() + LOCALE_AND_EXTRA_SEPARATOR
+ keyboardLayoutSetName; + keyboardLayoutSetName;
return extraValue.isEmpty() ? basePrefSubtype return extraValue.isEmpty() ? basePrefSubtype
: basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue; : basePrefSubtype + LOCALE_AND_EXTRA_SEPARATOR + extraValue;
} }
public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) { public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) {
@ -107,7 +109,7 @@ public final class AdditionalSubtypeUtils {
// use string created with getPrefSubtype // use string created with getPrefSubtype
public static InputMethodSubtype createSubtypeFromString(final String prefSubtype) { public static InputMethodSubtype createSubtypeFromString(final String prefSubtype) {
final String[] elems = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); final String[] elems = prefSubtype.split(LOCALE_AND_EXTRA_SEPARATOR);
if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE
&& elems.length != LENGTH_WITH_EXTRA_VALUE) { && elems.length != LENGTH_WITH_EXTRA_VALUE) {
Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype); Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype);

View file

@ -14,17 +14,17 @@ import helium314.keyboard.keyboard.KeyboardId
import helium314.keyboard.keyboard.KeyboardLayoutSet import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.keyboard.KeyboardSwitcher
import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.keyboard.internal.keyboard_parser.LayoutParser
import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL
import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser
import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.common.Constants
import helium314.keyboard.latin.common.FileUtils import helium314.keyboard.latin.common.FileUtils
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.LayoutType.Companion.folder
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.math.BigInteger import java.math.BigInteger
import java.util.EnumMap
fun loadCustomLayout(uri: Uri?, languageTag: String, context: Context, onAdded: (String) -> Unit) { fun loadCustomLayout(uri: Uri?, languageTag: String, context: Context, onAdded: (String) -> Unit) {
if (uri == null) if (uri == null)
@ -85,7 +85,7 @@ fun checkLayout(layoutContent: String, context: Context): Boolean {
params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT) params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT)
addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL) addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL)
try { try {
val keys = RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } } val keys = LayoutParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
return checkKeys(keys) return checkKeys(keys)
} catch (e: SerializationException) { } catch (e: SerializationException) {
Log.w(TAG, "json parsing error", e) Log.w(TAG, "json parsing error", e)
@ -96,13 +96,13 @@ fun checkLayout(layoutContent: String, context: Context): Boolean {
return false return false
} }
try { try {
val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { row -> row.map { it.toKeyParams(params) } } val keys = LayoutParser.parseSimpleString(layoutContent).map { row -> row.map { it.toKeyParams(params) } }
return checkKeys(keys) return checkKeys(keys)
} catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) } } catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) }
if (layoutContent.trimStart().startsWith("[") && layoutContent.trimEnd().endsWith("]")) { if (layoutContent.trimStart().startsWith("[") && layoutContent.trimEnd().endsWith("]")) {
// layout can't be loaded, assume it's json -> load json layout again because the error message shown to the user is from the most recent error // layout can't be loaded, assume it's json -> load json layout again because the error message shown to the user is from the most recent error
try { try {
RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } } LayoutParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
} catch (e: Exception) { Log.w(TAG, "json parsing error", e) } } catch (e: Exception) { Log.w(TAG, "json parsing error", e) }
} }
return false return false
@ -149,20 +149,28 @@ fun checkKeys(keys: List<List<Key.KeyParams>>): Boolean {
} }
/** don't rename or delete the file without calling [onCustomLayoutFileListChanged] */ /** don't rename or delete the file without calling [onCustomLayoutFileListChanged] */
fun getCustomLayoutFile(layoutName: String, context: Context) = fun getCustomLayoutFile(layoutName: String, context: Context) = // todo: remove
File(getCustomLayoutsDir(context), layoutName) File(getCustomLayoutsDir(context), layoutName)
// cache to avoid frequently listing files // cache to avoid frequently listing files
/** don't rename or delete files without calling [onCustomLayoutFileListChanged] */ /** don't rename or delete files without calling [onCustomLayoutFileListChanged] */
fun getCustomLayoutFiles(context: Context): List<File> { fun getCustomLayoutFiles(context: Context): List<File> { // todo: remove, AND USE THE NEW THING FOR SUBTYPE SETTINGS
customLayouts?.let { return it } customLayouts?.let { return it }
val layouts = getCustomLayoutsDir(context).listFiles()?.toList() ?: emptyList() val layouts = getCustomLayoutsDir(context).listFiles()?.toList() ?: emptyList()
customLayouts = layouts customLayouts = layouts
return layouts return layouts
} }
fun getCustomLayoutFiles(layoutType: LayoutType, context: Context): List<File> =
customLayoutMap.getOrPut(layoutType) {
File(DeviceProtectedUtils.getFilesDir(context), layoutType.folder).listFiles()?.toList() ?: emptyList()
}
private val customLayoutMap = EnumMap<LayoutType, List<File>>(LayoutType::class.java)
fun onCustomLayoutFileListChanged() { fun onCustomLayoutFileListChanged() {
customLayouts = null customLayouts = null
customLayoutMap.clear()
} }
private fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts") private fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts")
@ -215,45 +223,6 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
builder.show() builder.show()
} }
fun hasCustomFunctionalLayout(subtype: InputMethodSubtype, context: Context): Boolean {
val anyCustomFunctionalLayout = getCustomFunctionalLayoutName(KeyboardId.ELEMENT_ALPHABET, subtype, context)
?: getCustomFunctionalLayoutName(KeyboardId.ELEMENT_SYMBOLS, subtype, context)
?: getCustomFunctionalLayoutName(KeyboardId.ELEMENT_SYMBOLS_SHIFTED, subtype, context)
return anyCustomFunctionalLayout != null
}
fun getCustomFunctionalLayoutName(elementId: Int, subtype: InputMethodSubtype, context: Context): String? {
val customFunctionalLayoutNames = getCustomLayoutFiles(context).filter { it.name.contains("functional") }.map { it.name.substringBeforeLast(".") + "." }
if (customFunctionalLayoutNames.isEmpty()) return null
val languageTag = subtype.locale().toLanguageTag()
val mainLayoutName = subtype.mainLayoutName()
if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) }, mainLayoutName, languageTag)
?.let { return it }
}
if (elementId == KeyboardId.ELEMENT_SYMBOLS) {
findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS) }, mainLayoutName, languageTag)
?.let { return it }
}
return findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_NORMAL) }, mainLayoutName, languageTag)
}
// todo (when adding custom layouts per locale or main layout): adjust mainLayoutName for custom layouts?
// remove language tag and file ending (currently name is e.g. custom.en-US.abcdfdsg3.json, and we could use abcdfdsg3 only)
// this way, custom layouts with same name could use same custom functional layouts
// currently there is no way to set the language tag or main layout name, so changes don't break backwards compatibility
private fun findMatchingLayout(layoutNames: List<String>, mainLayoutName: String, languageTag: String): String? {
// first find layout with matching locale and main layout
return layoutNames.firstOrNull { it.endsWith(".$languageTag.$mainLayoutName.") }
// then find matching main layout
?: layoutNames.firstOrNull { it.endsWith(".$mainLayoutName.") }
// then find matching language
?: layoutNames.firstOrNull { it.endsWith(".$languageTag.") }
// then find "normal" functional layout (make use of the '.' separator
?: layoutNames.firstOrNull { it.count { it == '.' } == 2 }
}
fun encodeBase36(string: String): String = BigInteger(string.toByteArray()).toString(36) fun encodeBase36(string: String): String = BigInteger(string.toByteArray()).toString(36)
fun decodeBase36(string: String) = BigInteger(string, 36).toByteArray().decodeToString() fun decodeBase36(string: String) = BigInteger(string, 36).toByteArray().decodeToString()

View file

@ -1,5 +1,6 @@
package helium314.keyboard.latin.utils package helium314.keyboard.latin.utils
import java.io.File
import java.util.EnumMap import java.util.EnumMap
enum class LayoutType { enum class LayoutType {
@ -17,5 +18,7 @@ enum class LayoutType {
} }
return map return map
} }
val LayoutType.folder get() = "layouts${File.separator}${name.lowercase()}${File.separator}"
} }
} }

View file

@ -273,7 +273,7 @@ public final class SubtypeLocaleUtils {
@NonNull @NonNull
public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) { public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) {
String keyboardLayoutSet = SubtypeUtilsKt.explicitMainLayoutName(subtype); String keyboardLayoutSet = SubtypeUtilsKt.mainLayoutName(subtype);
if (keyboardLayoutSet == null && subtype.isAsciiCapable()) { if (keyboardLayoutSet == null && subtype.isAsciiCapable()) {
keyboardLayoutSet = QWERTY; keyboardLayoutSet = QWERTY;
} }

View file

@ -13,7 +13,6 @@ import androidx.core.content.edit
import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.keyboard.KeyboardSwitcher
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.RichInputMethodManager import helium314.keyboard.latin.RichInputMethodManager
import helium314.keyboard.latin.common.Constants
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
@ -41,7 +40,7 @@ fun getAllAvailableSubtypes(): List<InputMethodSubtype> {
fun getMatchingLayoutSetNameForLocale(locale: Locale): String { fun getMatchingLayoutSetNameForLocale(locale: Locale): String {
val subtypes = resourceSubtypesByLocale.values.flatten() val subtypes = resourceSubtypesByLocale.values.flatten()
val name = LocaleUtils.getBestMatch(locale, subtypes) { it.locale() }?.explicitMainLayoutName() val name = LocaleUtils.getBestMatch(locale, subtypes) { it.locale() }?.mainLayoutName()
if (name != null) return name if (name != null) return name
return when (locale.script()) { return when (locale.script()) {
ScriptUtils.SCRIPT_LATIN -> "qwerty" ScriptUtils.SCRIPT_LATIN -> "qwerty"
@ -246,7 +245,7 @@ private fun loadResourceSubtypes(resources: Resources) {
private fun removeInvalidCustomSubtypes(context: Context) { private fun removeInvalidCustomSubtypes(context: Context) {
val prefs = context.prefs() val prefs = context.prefs()
val additionalSubtypes = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!.split(";") val additionalSubtypes = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!.split(";")
val customSubtypeFiles by lazy { getCustomLayoutFiles(context).map { it.name } } val customSubtypeFiles by lazy { getCustomLayoutFiles(LayoutType.MAIN, context).map { it.name } }
val subtypesToRemove = mutableListOf<String>() val subtypesToRemove = mutableListOf<String>()
additionalSubtypes.forEach { additionalSubtypes.forEach {
val name = it.substringAfter(":").substringBefore(":") val name = it.substringAfter(":").substringBefore(":")

View file

@ -16,12 +16,7 @@ fun InputMethodSubtype.locale(): Locale {
@Suppress("deprecation") return locale.constructLocale() @Suppress("deprecation") return locale.constructLocale()
} }
fun InputMethodSubtype.mainLayoutName(): String { fun InputMethodSubtype.mainLayoutName(): String? {
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
return map[LayoutType.MAIN] ?: "qwerty"
}
fun InputMethodSubtype.explicitMainLayoutName(): String? {
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "") val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
return map[LayoutType.MAIN] return map[LayoutType.MAIN]
} }

View file

@ -74,13 +74,11 @@ object SettingsWithoutKey {
const val GITHUB = "github" const val GITHUB = "github"
const val SAVE_LOG = "save_log" const val SAVE_LOG = "save_log"
const val CUSTOM_KEY_CODES = "customize_key_codes" const val CUSTOM_KEY_CODES = "customize_key_codes"
const val CUSTOM_SYMBOLS_NUMBER_LAYOUTS = "custom_symbols_number_layouts" // const val CUSTOM_SYMBOLS_NUMBER_LAYOUTS = "custom_symbols_number_layouts"
const val CUSTOM_FUNCTIONAL_LAYOUTS = "custom_functional_key_layouts" // const val CUSTOM_FUNCTIONAL_LAYOUTS = "custom_functional_key_layouts"
const val BACKUP_RESTORE = "backup_restore" const val BACKUP_RESTORE = "backup_restore"
const val DEBUG_SETTINGS = "screen_debug" const val DEBUG_SETTINGS = "screen_debug"
const val LOAD_GESTURE_LIB = "load_gesture_library" const val LOAD_GESTURE_LIB = "load_gesture_library"
const val ADJUST_COLORS = "adjust_colors"
const val ADJUST_COLORS_NIGHT = "adjust_colors_night"
const val BACKGROUND_IMAGE = "background_image" const val BACKGROUND_IMAGE = "background_image"
const val BACKGROUND_IMAGE_LANDSCAPE = "background_image_landscape" const val BACKGROUND_IMAGE_LANDSCAPE = "background_image_landscape"
const val CUSTOM_FONT = "custom_font" const val CUSTOM_FONT = "custom_font"

View file

@ -20,7 +20,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import helium314.keyboard.keyboard.KeyboardActionListener import helium314.keyboard.keyboard.KeyboardActionListener
import helium314.keyboard.keyboard.KeyboardLayoutSet import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser
import helium314.keyboard.latin.BuildConfig import helium314.keyboard.latin.BuildConfig
import helium314.keyboard.latin.R import helium314.keyboard.latin.R
import helium314.keyboard.latin.SystemBroadcastReceiver import helium314.keyboard.latin.SystemBroadcastReceiver
@ -74,8 +73,6 @@ fun AdvancedSettingsScreen(
Settings.PREF_ABC_AFTER_CLIP, Settings.PREF_ABC_AFTER_CLIP,
Settings.PREF_CUSTOM_CURRENCY_KEY, Settings.PREF_CUSTOM_CURRENCY_KEY,
Settings.PREF_MORE_POPUP_KEYS, Settings.PREF_MORE_POPUP_KEYS,
SettingsWithoutKey.CUSTOM_SYMBOLS_NUMBER_LAYOUTS,
SettingsWithoutKey.CUSTOM_FUNCTIONAL_LAYOUTS,
SettingsWithoutKey.BACKUP_RESTORE, SettingsWithoutKey.BACKUP_RESTORE,
if (BuildConfig.DEBUG || prefs.getBoolean(DebugSettings.PREF_SHOW_DEBUG_SETTINGS, Defaults.PREF_SHOW_DEBUG_SETTINGS)) if (BuildConfig.DEBUG || prefs.getBoolean(DebugSettings.PREF_SHOW_DEBUG_SETTINGS, Defaults.PREF_SHOW_DEBUG_SETTINGS))
SettingsWithoutKey.DEBUG_SETTINGS else null, SettingsWithoutKey.DEBUG_SETTINGS else null,
@ -195,7 +192,7 @@ fun createAdvancedSettings(context: Context) = listOf(
) )
ListPreference(it, items, Defaults.PREF_MORE_POPUP_KEYS) { KeyboardLayoutSet.onSystemLocaleChanged() } ListPreference(it, items, Defaults.PREF_MORE_POPUP_KEYS) { KeyboardLayoutSet.onSystemLocaleChanged() }
}, },
Setting(context, SettingsWithoutKey.CUSTOM_SYMBOLS_NUMBER_LAYOUTS, R.string.customize_symbols_number_layouts) { setting -> /* Setting(context, SettingsWithoutKey.CUSTOM_SYMBOLS_NUMBER_LAYOUTS, R.string.customize_symbols_number_layouts) { setting ->
LayoutEditPreference( LayoutEditPreference(
setting = setting, setting = setting,
items = RawKeyboardParser.symbolAndNumberLayouts, items = RawKeyboardParser.symbolAndNumberLayouts,
@ -211,7 +208,7 @@ fun createAdvancedSettings(context: Context) = listOf(
getItemName = { it.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", LocalContext.current) }, getItemName = { it.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", LocalContext.current) },
getDefaultLayout = { if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" } getDefaultLayout = { if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" }
) )
}, },*/ // todo: these settings are disabled for now -> remove them and use a layoutScreen instead
Setting(context, SettingsWithoutKey.BACKUP_RESTORE, R.string.backup_restore_title) { Setting(context, SettingsWithoutKey.BACKUP_RESTORE, R.string.backup_restore_title) {
BackupRestorePreference(it) BackupRestorePreference(it)
}, },

View file

@ -166,7 +166,7 @@
android:imeSubtypeLocale="ar" android:imeSubtypeLocale="ar"
android:languageTag="ar" android:languageTag="ar"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:arabic,NoShiftKey,SupportTouchPositionCorrection,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:arabic|SYMBOLS:symbols_arabic,NoShiftKey,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<subtype android:icon="@drawable/ic_ime_switcher" <subtype android:icon="@drawable/ic_ime_switcher"
@ -175,7 +175,7 @@
android:imeSubtypeLocale="ar" android:imeSubtypeLocale="ar"
android:languageTag="ar" android:languageTag="ar"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:arabic_pc,NoShiftKey,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:arabic_pc|SYMBOLS:symbols_arabic,NoShiftKey,EmojiCapable"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<subtype android:icon="@drawable/ic_ime_switcher" <subtype android:icon="@drawable/ic_ime_switcher"
@ -184,7 +184,7 @@
android:imeSubtypeLocale="ar" android:imeSubtypeLocale="ar"
android:languageTag="ar" android:languageTag="ar"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:arabic_hijai,NoShiftKey,SupportTouchPositionCorrection,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:arabic_hijai|SYMBOLS:symbols_arabic,NoShiftKey,SupportTouchPositionCorrection,EmojiCapable"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<subtype android:icon="@drawable/ic_ime_switcher" <subtype android:icon="@drawable/ic_ime_switcher"
@ -409,7 +409,7 @@
android:imeSubtypeLocale="fa" android:imeSubtypeLocale="fa"
android:languageTag="fa" android:languageTag="fa"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:farsi,NoShiftKey,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:farsi|SYMBOLS:symbols_arabic,NoShiftKey,EmojiCapable"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<subtype android:icon="@drawable/ic_ime_switcher" <subtype android:icon="@drawable/ic_ime_switcher"
@ -1060,7 +1060,7 @@
android:imeSubtypeLocale="ur_PK" android:imeSubtypeLocale="ur_PK"
android:languageTag="ur-PK" android:languageTag="ur-PK"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:urdu,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:urdu|SYMBOLS:symbols_arabic,EmojiCapable"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<!-- This Uzbek keyboard is a preliminary layout. <!-- This Uzbek keyboard is a preliminary layout.

View file

@ -8,14 +8,13 @@ import helium314.keyboard.keyboard.Key.KeyParams
import helium314.keyboard.keyboard.Keyboard import helium314.keyboard.keyboard.Keyboard
import helium314.keyboard.keyboard.KeyboardId import helium314.keyboard.keyboard.KeyboardId
import helium314.keyboard.keyboard.KeyboardLayoutSet import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.keyboard.internal.KeySpecParser
import helium314.keyboard.keyboard.internal.KeySpecParser.KeySpecParserError import helium314.keyboard.keyboard.internal.KeySpecParser.KeySpecParserError
import helium314.keyboard.keyboard.internal.KeyboardBuilder import helium314.keyboard.keyboard.internal.KeyboardBuilder
import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.keyboard.internal.TouchPositionCorrection import helium314.keyboard.keyboard.internal.TouchPositionCorrection
import helium314.keyboard.keyboard.internal.UniqueKeysCache import helium314.keyboard.keyboard.internal.UniqueKeysCache
import helium314.keyboard.keyboard.internal.keyboard_parser.LayoutParser
import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL
import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser
import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
import helium314.keyboard.latin.LatinIME import helium314.keyboard.latin.LatinIME
@ -119,7 +118,7 @@ f""", // no newline at the end
val wantedKeyLabels = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) val wantedKeyLabels = listOf(listOf("a", "b", "c"), listOf("d", "e", "f"))
layoutStrings.forEachIndexed { i, layout -> layoutStrings.forEachIndexed { i, layout ->
println(i) println(i)
val keyLabels = RawKeyboardParser.parseSimpleString(layout).map { it.map { it.toKeyParams(params).mLabel } } val keyLabels = LayoutParser.parseSimpleString(layout).map { it.map { it.toKeyParams(params).mLabel } }
assertEquals(wantedKeyLabels, keyLabels) assertEquals(wantedKeyLabels, keyLabels)
} }
} }
@ -321,12 +320,12 @@ f""", // no newline at the end
@Test fun invalidKeys() { @Test fun invalidKeys() {
assertFailsWith<KeySpecParserError> { assertFailsWith<KeySpecParserError> {
RawKeyboardParser.parseJsonString("""[[{ "label": "!icon/clipboard_action_key" }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } LayoutParser.parseJsonString("""[[{ "label": "!icon/clipboard_action_key" }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } }
} }
} }
@Test fun popupWithCodeAndLabel() { @Test fun popupWithCodeAndLabel() {
val key = RawKeyboardParser.parseJsonString("""[[{ "label": "w", "popup": { val key = LayoutParser.parseJsonString("""[[{ "label": "w", "popup": {
"main": { "code": 55, "label": "!" } "main": { "code": 55, "label": "!" }
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
assertEquals("!", key.toKeyParams(params).mPopupKeys?.first()?.mLabel) assertEquals("!", key.toKeyParams(params).mPopupKeys?.first()?.mLabel)
@ -334,7 +333,7 @@ f""", // no newline at the end
} }
@Test fun popupWithCodeAndIcon() { @Test fun popupWithCodeAndIcon() {
val key = RawKeyboardParser.parseJsonString("""[[{ "label": "w", "popup": { val key = LayoutParser.parseJsonString("""[[{ "label": "w", "popup": {
"main": { "code": 55, "label": "!icon/clipboard_action_key" } "main": { "code": 55, "label": "!icon/clipboard_action_key" }
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
assertEquals(null, key.toKeyParams(params).mPopupKeys?.first()?.mLabel) assertEquals(null, key.toKeyParams(params).mPopupKeys?.first()?.mLabel)
@ -343,7 +342,7 @@ f""", // no newline at the end
} }
@Test fun popupToolbarKey() { @Test fun popupToolbarKey() {
val key = RawKeyboardParser.parseJsonString("""[[{ "label": "x", "popup": { val key = LayoutParser.parseJsonString("""[[{ "label": "x", "popup": {
"main": { "label": "undo" } "main": { "label": "undo" }
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
assertEquals(null, key.toKeyParams(params).mPopupKeys?.first()?.mLabel) assertEquals(null, key.toKeyParams(params).mPopupKeys?.first()?.mLabel)
@ -352,7 +351,7 @@ f""", // no newline at the end
} }
@Test fun popupKeyWithIconAndImplicitText() { @Test fun popupKeyWithIconAndImplicitText() {
val key = RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [ val key = LayoutParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [
{ "label": "!icon/go_key|aa" } { "label": "!icon/go_key|aa" }
] ]
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
@ -361,7 +360,7 @@ f""", // no newline at the end
assertEquals(KeyCode.MULTIPLE_CODE_POINTS, key.toKeyParams(params).mPopupKeys?.first()?.mCode) assertEquals(KeyCode.MULTIPLE_CODE_POINTS, key.toKeyParams(params).mPopupKeys?.first()?.mCode)
assertEquals("aa", key.toKeyParams(params).mPopupKeys?.first()?.mOutputText) assertEquals("aa", key.toKeyParams(params).mPopupKeys?.first()?.mOutputText)
val key2 = RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [ val key2 = LayoutParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [
{ "label": "!icon/go_key|" } { "label": "!icon/go_key|" }
] ]
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
@ -373,7 +372,7 @@ f""", // no newline at the end
// output text is null here, maybe should be changed? // output text is null here, maybe should be changed?
@Test fun popupKeyWithIconAndCodeAndImplicitText() { @Test fun popupKeyWithIconAndCodeAndImplicitText() {
val key = RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [ val key = LayoutParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [
{ "label": "!icon/go_key|", "code": 55 } { "label": "!icon/go_key|", "code": 55 }
] ]
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
@ -382,7 +381,7 @@ f""", // no newline at the end
assertEquals(55, key.toKeyParams(params).mPopupKeys?.first()?.mCode) assertEquals(55, key.toKeyParams(params).mPopupKeys?.first()?.mCode)
assertEquals(null, key.toKeyParams(params).mPopupKeys?.first()?.mOutputText) assertEquals(null, key.toKeyParams(params).mPopupKeys?.first()?.mOutputText)
val key2 = RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [ val key2 = LayoutParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [
{ "label": "!icon/go_key|a", "code": 55 } { "label": "!icon/go_key|a", "code": 55 }
] ]
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
@ -391,7 +390,7 @@ f""", // no newline at the end
assertEquals(55, key2.toKeyParams(params).mPopupKeys?.first()?.mCode) assertEquals(55, key2.toKeyParams(params).mPopupKeys?.first()?.mCode)
assertEquals(null, key2.toKeyParams(params).mPopupKeys?.first()?.mOutputText) assertEquals(null, key2.toKeyParams(params).mPopupKeys?.first()?.mOutputText)
val key3 = RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [ val key3 = LayoutParser.parseJsonString("""[[{ "label": "a", "popup": { "relevant": [
{ "label": "!icon/go_key|aa", "code": 55 } { "label": "!icon/go_key|aa", "code": 55 }
] ]
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
@ -403,14 +402,14 @@ f""", // no newline at the end
@Test fun invalidPopupKeys() { @Test fun invalidPopupKeys() {
assertFailsWith<KeySpecParserError> { assertFailsWith<KeySpecParserError> {
RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { LayoutParser.parseJsonString("""[[{ "label": "a", "popup": {
"main": { "label": "!icon/clipboard_action_key" } "main": { "label": "!icon/clipboard_action_key" }
} }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } } }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } }
} }
} }
@Test fun popupSymbolAlpha() { @Test fun popupSymbolAlpha() {
val key = RawKeyboardParser.parseJsonString("""[[{ "label": "c", "popup": { val key = LayoutParser.parseJsonString("""[[{ "label": "c", "popup": {
"main": { "code": -10001, "label": "x" } "main": { "code": -10001, "label": "x" }
} }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single() } }]]""").map { it.mapNotNull { it.compute(params) } }.flatten().single()
assertEquals("x", key.toKeyParams(params).mPopupKeys?.first()?.mLabel) assertEquals("x", key.toKeyParams(params).mPopupKeys?.first()?.mLabel)
@ -462,8 +461,8 @@ f""", // no newline at the end
latinIME.assets.list("layouts")?.forEach { latinIME.assets.list("layouts")?.forEach {
val content = latinIME.assets.open("layouts/$it").reader().readText() val content = latinIME.assets.open("layouts/$it").reader().readText()
if (it.endsWith(".json")) if (it.endsWith(".json"))
RawKeyboardParser.parseJsonString(content) LayoutParser.parseJsonString(content)
else RawKeyboardParser.parseSimpleString(content) else LayoutParser.parseSimpleString(content)
} }
} }
@ -474,7 +473,7 @@ f""", // no newline at the end
} }
private fun assertAreExpected(json: String, expected: List<Expected>) { private fun assertAreExpected(json: String, expected: List<Expected>) {
val keys = RawKeyboardParser.parseJsonString(json).map { it.mapNotNull { it.compute(params) } }.flatten() val keys = LayoutParser.parseJsonString(json).map { it.mapNotNull { it.compute(params) } }.flatten()
keys.forEachIndexed { index, keyData -> keys.forEachIndexed { index, keyData ->
println("data: key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}") println("data: key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}")
val keyParams = keyData.toKeyParams(params) val keyParams = keyData.toKeyParams(params)