From 8123c257aea12cb376c74f013d9c4559ec4fb8eb Mon Sep 17 00:00:00 2001 From: nopdan Date: Sun, 4 Feb 2024 01:31:35 +0800 Subject: [PATCH] perf: dynamically load and cache keyboard layout --- .../java/com/osfans/trime/data/theme/Theme.kt | 50 +----- .../com/osfans/trime/ime/keyboard/Keyboard.kt | 38 +++- .../trime/ime/keyboard/KeyboardSwitcher.kt | 167 ++++++++++-------- 3 files changed, 131 insertions(+), 124 deletions(-) diff --git a/app/src/main/java/com/osfans/trime/data/theme/Theme.kt b/app/src/main/java/com/osfans/trime/data/theme/Theme.kt index 85a4cc59d7..36c50122b3 100644 --- a/app/src/main/java/com/osfans/trime/data/theme/Theme.kt +++ b/app/src/main/java/com/osfans/trime/data/theme/Theme.kt @@ -23,7 +23,6 @@ import androidx.core.math.MathUtils import com.osfans.trime.core.Rime import com.osfans.trime.data.AppPrefs import com.osfans.trime.data.DataManager.userDataDir -import com.osfans.trime.data.schema.SchemaManager import com.osfans.trime.data.sound.SoundThemeManager import com.osfans.trime.ime.keyboard.Key import com.osfans.trime.util.CollectionUtils @@ -43,6 +42,7 @@ class Theme(private var isDarkMode: Boolean) { private var presetColorSchemes: Map?>? = null private var presetKeyboards: Map? = null private var liquidKeyboard: Map? = null + lateinit var allKeyboardIds: List // 当前配色 id lateinit var currentColorSchemeId: String @@ -109,10 +109,16 @@ class Theme(private var isDarkMode: Boolean) { presetColorSchemes = fullThemeConfigMap!!["preset_color_schemes"] as Map?>? presetKeyboards = fullThemeConfigMap!!["preset_keyboards"] as Map? liquidKeyboard = fullThemeConfigMap!!["liquid_keyboard"] as Map? - }.also { Timber.d("Setting up all theme config map takes $it ms") } + // 将 presetKeyboards 的所有 key 转为 allKeyboardIds + allKeyboardIds = presetKeyboards!!.keys.toList() + }.also { + Timber.d("Setting up all theme config map takes $it ms") + } measureTimeMillis { initColorScheme() - }.also { Timber.d("Initializing cache takes $it ms") } + }.also { + Timber.d("Initializing cache takes $it ms") + } Timber.i("The theme is initialized") }.getOrElse { Timber.e("Failed to parse the theme: ${it.message}") @@ -245,44 +251,6 @@ class Theme(private var isDarkMode: Boolean) { fun getObject(key: String): Any? { return CollectionUtils.obtainValue(theme.presetKeyboards, key) } - - fun remapKeyboardId(name: String): String { - val remapped = - if (".default" == name) { - val currentSchemaId = Rime.getCurrentRimeSchema() - if (theme.presetKeyboards!!.containsKey(currentSchemaId)) { - return currentSchemaId - } else { - val alphabet = SchemaManager.getActiveSchema().alphabet - val twentySix = "qwerty" - if (!alphabet.isNullOrEmpty() && theme.presetKeyboards!!.containsKey(alphabet)) { - return alphabet - } else { - if (!alphabet.isNullOrEmpty() && (alphabet.contains(",") || alphabet.contains(";"))) { - twentySix + "_" - } else if (!alphabet.isNullOrEmpty() && (alphabet.contains("0") || alphabet.contains("1"))) { - twentySix + "0" - } else { - twentySix - } - } - } - } else { - name - } - if (!theme.presetKeyboards!!.containsKey(remapped)) { - Timber.w("Cannot find keyboard definition %s, fallback ...", remapped) - val defaultMap = - theme.presetKeyboards!!["default"] as Map? - ?: throw IllegalStateException("The default keyboard definition is missing!") - return if (defaultMap.containsKey("import_preset")) { - defaultMap["import_preset"] as? String ?: "default" - } else { - "default" - } - } - return remapped - } } /** diff --git a/app/src/main/java/com/osfans/trime/ime/keyboard/Keyboard.kt b/app/src/main/java/com/osfans/trime/ime/keyboard/Keyboard.kt index 0d14575044..2bc3a3b203 100644 --- a/app/src/main/java/com/osfans/trime/ime/keyboard/Keyboard.kt +++ b/app/src/main/java/com/osfans/trime/ime/keyboard/Keyboard.kt @@ -154,16 +154,26 @@ class Keyboard() { height = y + keyHeight } - constructor(name: String?) : this() { + private fun getKeyboardConfig(name: String): Map? { val theme = ThemeManager.activeTheme - val keyboardConfig: Map? - val v = theme.keyboards.getObject(name!!) - keyboardConfig = + val v = theme.keyboards.getObject(name) + val keyboardConfig = if (v != null) { v as Map? } else { theme.keyboards.getObject("default") as Map? } + val importPreset = keyboardConfig?.get("import_preset") as String? + if (importPreset != null) { + return getKeyboardConfig(importPreset) + } + return keyboardConfig + } + + constructor(name: String) : this() { + val theme = ThemeManager.activeTheme + val keyboardConfig = getKeyboardConfig(name) + mLabelTransform = obtainString(keyboardConfig, "label_transform", "none") mAsciiMode = obtainInt(keyboardConfig, "ascii_mode", 1) if (mAsciiMode == 0) asciiKeyboard = obtainString(keyboardConfig, "ascii_keyboard", "") @@ -195,18 +205,24 @@ class Keyboard() { horizontalGap = sp2px( obtainFloat( - keyboardConfig, "horizontal_gap", theme.style.getFloat("horizontal_gap"), + keyboardConfig, + "horizontal_gap", + theme.style.getFloat("horizontal_gap"), ), ).toInt() verticalGap = sp2px( obtainFloat( - keyboardConfig, "vertical_gap", theme.style.getFloat("vertical_gap"), + keyboardConfig, + "vertical_gap", + theme.style.getFloat("vertical_gap"), ), ).toInt() roundCorner = obtainFloat( - keyboardConfig, "round_corner", theme.style.getFloat("round_corner"), + keyboardConfig, + "round_corner", + theme.style.getFloat("round_corner"), ) val horizontalGap = horizontalGap val verticalGap = verticalGap @@ -361,13 +377,17 @@ class Keyboard() { key.key_symbol_offset_x = sp2px( obtainFloat( - mk, "key_symbol_offset_x", defaultKeySymbolOffsetX.toFloat(), + mk, + "key_symbol_offset_x", + defaultKeySymbolOffsetX.toFloat(), ), ).toInt() key.key_symbol_offset_y = sp2px( obtainFloat( - mk, "key_symbol_offset_y", defaultKeySymbolOffsetY.toFloat(), + mk, + "key_symbol_offset_y", + defaultKeySymbolOffsetY.toFloat(), ), ).toInt() key.key_hint_offset_x = diff --git a/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardSwitcher.kt b/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardSwitcher.kt index 14d4bf7017..6b9d67af65 100644 --- a/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardSwitcher.kt +++ b/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardSwitcher.kt @@ -1,130 +1,149 @@ package com.osfans.trime.ime.keyboard import android.content.res.Configuration +import com.osfans.trime.core.Rime import com.osfans.trime.data.AppPrefs +import com.osfans.trime.data.schema.SchemaManager import com.osfans.trime.data.theme.ThemeManager import com.osfans.trime.util.appContext import timber.log.Timber /** Manages [Keyboard]s and their status. **/ object KeyboardSwitcher { - private var currentId: Int = 0 - private var lastId: Int = 0 - private var lastLockId: Int = 0 - + private val theme get() = ThemeManager.activeTheme + private val allKeyboardIds get() = theme.allKeyboardIds + private val keyboardCache: MutableMap = mutableMapOf() + private var currentKeyboardId = ".default" + private var lastKeyboardId = ".default" + private var lastLockKeyboardId = ".default" private var currentDisplayWidth: Int = 0 private val keyboardPrefs = KeyboardPrefs() - private val theme get() = ThemeManager.activeTheme - private lateinit var availableKeyboardIds: List - private lateinit var availableKeyboards: List + lateinit var currentKeyboard: Keyboard - /** To get current keyboard instance. **/ - @JvmStatic - val currentKeyboard: Keyboard - get() { - if (currentId >= availableKeyboards.size) currentId = 0 - return availableKeyboards[currentId] + private fun getKeyboard(name: String): Keyboard { + if (keyboardCache.containsKey(name)) { + return keyboardCache[name]!! } + val keyboard = Keyboard(name) + keyboardCache[name] = keyboard + return keyboard + } init { newOrReset() } - @Suppress("UNCHECKED_CAST") @JvmStatic fun newOrReset() { Timber.d("Switching keyboard back to .default ...") - availableKeyboardIds = (theme.style.getObject("keyboards") as? List) - ?.map { theme.keyboards.remapKeyboardId(it) }?.distinct() ?: listOf() - - availableKeyboards = - availableKeyboardIds.map { - try { - Keyboard(theme.keyboards.remapKeyboardId(it)) - } catch (e: Exception) { - Keyboard("default") - } - } - - currentId = 0 - lastId = 0 - lastLockId = 0 + currentKeyboardId = ".default" + lastKeyboardId = ".default" + lastLockKeyboardId = ".default" currentDisplayWidth = 0 + keyboardCache.clear() + switchKeyboard(currentKeyboardId) } fun switchKeyboard(name: String?) { - val idx = + val currentIdx = theme.allKeyboardIds.indexOf(currentKeyboardId) + var mappedName = when (name) { - ".default" -> 0 - ".prior" -> currentId - 1 - ".next" -> currentId + 1 - ".last" -> lastId - ".last_lock" -> lastLockId + ".default" -> autoMatch(name) + ".prior" -> + try { + theme.allKeyboardIds[currentIdx - 1] + } catch (e: IndexOutOfBoundsException) { + currentKeyboardId + } + ".next" -> + try { + theme.allKeyboardIds[currentIdx + 1] + } catch (e: IndexOutOfBoundsException) { + currentKeyboardId + } + ".last" -> lastKeyboardId + ".last_lock" -> lastLockKeyboardId ".ascii" -> { - val asciiKeyboard = availableKeyboards[currentId].asciiKeyboard - if (asciiKeyboard.isNullOrEmpty()) { - currentId + val asciiKeyboard = currentKeyboard.asciiKeyboard + if (asciiKeyboard != null && asciiKeyboard in allKeyboardIds) { + asciiKeyboard } else { - availableKeyboardIds.indexOf(asciiKeyboard) + currentKeyboardId } } else -> { if (name.isNullOrEmpty()) { - if (availableKeyboards[currentId].isLock) currentId else lastLockId + if (currentKeyboard.isLock) currentKeyboardId else lastLockKeyboardId } else { - availableKeyboardIds.indexOf(name) + name } } } - val i = - if (keyboardPrefs.isLandscapeMode()) { - mapToLandscapeKeyboardIdx(idx) - } else { - idx - } - - Timber.d("Mapped from %d to %d", idx, i) + // 切换到 mini 键盘 val deviceKeyboard = appContext.resources.configuration.keyboard - if (currentId >= 0 && availableKeyboards[currentId].isLock) { - lastLockId = currentId + if (AppPrefs.defaultInstance().theme.useMiniKeyboard && deviceKeyboard != Configuration.KEYBOARD_NOKEYS) { + if ("mini" in allKeyboardIds) mappedName = "mini" } - lastId = currentId - - currentId = if (i >= availableKeyboardIds.size || i < 0) 0 else i - if ("mini" in availableKeyboardIds) { - val mini = availableKeyboardIds.indexOf("mini") - currentId = - if (AppPrefs.defaultInstance().theme.useMiniKeyboard && deviceKeyboard != Configuration.KEYBOARD_NOKEYS) { - if (currentId == 0) mini else currentId - } else { - if (currentId == mini) 0 else currentId - } + // 切换到横屏布局 + if (keyboardPrefs.isLandscapeMode()) { + val landscapeKeyboard = getKeyboard(mappedName).landscapeKeyboard + if (landscapeKeyboard != null && landscapeKeyboard in allKeyboardIds) { + mappedName = landscapeKeyboard + } } - + // 应用键盘布局 Timber.i( - "Switched keyboard from ${availableKeyboardIds[lastId]} " + - "to ${availableKeyboardIds[currentId]} (deviceKeyboard=$deviceKeyboard).", + "Switched keyboard from $currentKeyboardId " + + "to $mappedName (deviceKeyboard=$deviceKeyboard).", ) + currentKeyboardId = mappedName + currentKeyboard = getKeyboard(currentKeyboardId) + + if (currentKeyboard.isLock) { + lastLockKeyboardId = currentKeyboardId + } + lastKeyboardId = currentKeyboardId } - private fun mapToLandscapeKeyboardIdx(idx: Int): Int { - if (idx < 0 || idx > availableKeyboards.size) return idx - val landscapeKeyboard = availableKeyboards[idx].landscapeKeyboard - return if (landscapeKeyboard.isNullOrBlank()) { - idx - } else { - availableKeyboardIds.indexOf(landscapeKeyboard) + /** + * .default 自动匹配键盘布局 + * */ + private fun autoMatch(name: String): String { + // 主题的布局中包含方案id,直接采用 + val currentSchemaId = Rime.getCurrentRimeSchema() + if (currentSchemaId in allKeyboardIds) { + return currentSchemaId } + // 获取方案中的 alphabet(包含所有用到的按键 + val alphabet = SchemaManager.getActiveSchema().alphabet + if (alphabet.isNullOrEmpty()) return "default" + + val layout = + if (alphabet.matches(Regex("^[a-z]+$"))) { + // 包含 26 个字母 + "qwerty" + } else if (alphabet.matches(Regex("^[a-z,./;]+$"))) { + // 包含 26 个字母和,./; + "qwerty_" + } else if (alphabet.matches(Regex("^[a-z0-9]+$"))) { + // 包含 26 个字母和数字键 + "qwerty0" + } else { + null + } + if (layout != null && layout in allKeyboardIds) return layout + + Timber.d("Could not find keyboard layout $layout, fallback to default") + return "default" } /** * Change current display width when e.g. rotate the screen. */ fun resize(displayWidth: Int) { - if (currentId >= 0 && (displayWidth == currentDisplayWidth)) return - + if (displayWidth == currentDisplayWidth) return currentDisplayWidth = displayWidth newOrReset() }