diff --git a/app/src/main/java/com/osfans/trime/TrimeApplication.kt b/app/src/main/java/com/osfans/trime/TrimeApplication.kt index 2313c65308..985fd9a23b 100644 --- a/app/src/main/java/com/osfans/trime/TrimeApplication.kt +++ b/app/src/main/java/com/osfans/trime/TrimeApplication.kt @@ -24,10 +24,10 @@ class TrimeApplication : Application() { private var instance: TrimeApplication? = null private var lastPid: Int? = null - fun getInstance() = - instance ?: throw IllegalStateException("Trime application is not created!") + fun getInstance() = instance ?: throw IllegalStateException("Trime application is not created!") fun getLastPid() = lastPid + private const val MAX_STACKTRACE_SIZE = 128000 } @@ -40,13 +40,14 @@ class TrimeApplication : Application() { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra(LogActivity.FROM_CRASH, true) // avoid transaction overflow - val truncated = e.stackTraceToString().let { - if (it.length > MAX_STACKTRACE_SIZE) { - it.take(MAX_STACKTRACE_SIZE) + "" - } else { - it + val truncated = + e.stackTraceToString().let { + if (it.length > MAX_STACKTRACE_SIZE) { + it.take(MAX_STACKTRACE_SIZE) + "" + } else { + it + } } - } putExtra(LogActivity.CRASH_STACK_TRACE, truncated) }, ) @@ -60,27 +61,41 @@ class TrimeApplication : Application() { initDefaultPreferences() } if (BuildConfig.DEBUG) { - Timber.plant(object : Timber.DebugTree() { - override fun createStackElementTag(element: StackTraceElement): String { - return "${super.createStackElementTag(element)}|${element.fileName}:${element.lineNumber}" - } + Timber.plant( + object : Timber.DebugTree() { + override fun createStackElementTag(element: StackTraceElement): String { + return "${super.createStackElementTag(element)}|${element.fileName}:${element.lineNumber}" + } - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - super.log( - priority, - "[${Thread.currentThread().name}] ${tag?.substringBefore('|')}", - "${tag?.substringAfter('|')}] $message", - t, - ) - } - }) + override fun log( + priority: Int, + tag: String?, + message: String, + t: Throwable?, + ) { + super.log( + priority, + "[${Thread.currentThread().name}] ${tag?.substringBefore('|')}", + "${tag?.substringAfter('|')}] $message", + t, + ) + } + }, + ) } else { - Timber.plant(object : Timber.Tree() { - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - if (priority < Log.INFO) return - Log.println(priority, "[${Thread.currentThread().name}]", message) - } - }) + Timber.plant( + object : Timber.Tree() { + override fun log( + priority: Int, + tag: String?, + message: String, + t: Throwable?, + ) { + if (priority < Log.INFO) return + Log.println(priority, "[${Thread.currentThread().name}]", message) + } + }, + ) } // record last pid for crash logs val appPrefs = AppPrefs.defaultInstance() diff --git a/app/src/main/java/com/osfans/trime/core/Rime.kt b/app/src/main/java/com/osfans/trime/core/Rime.kt index 29e83f581b..dad55f7ac9 100644 --- a/app/src/main/java/com/osfans/trime/core/Rime.kt +++ b/app/src/main/java/com/osfans/trime/core/Rime.kt @@ -51,10 +51,11 @@ class Rime(fullCheck: Boolean) { private var mContext: RimeContext? = null private var mStatus: RimeStatus? = null private var isHandlingRimeNotification = false - private val notificationFlow_ = MutableSharedFlow( - extraBufferCapacity = 15, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) + private val notificationFlow_ = + MutableSharedFlow( + extraBufferCapacity = 15, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) init { System.loadLibrary("rime_jni") @@ -175,13 +176,16 @@ class Rime(fullCheck: Boolean) { @JvmStatic fun isVoidKeycode(keycode: Int): Boolean { - val XK_VoidSymbol = 0xffffff - return keycode <= 0 || keycode == XK_VoidSymbol + val voidSymbol = 0xffffff + return keycode <= 0 || keycode == voidSymbol } // KeyProcess 调用JNI方法发送keycode和mask @JvmStatic - fun processKey(keycode: Int, mask: Int): Boolean { + fun processKey( + keycode: Int, + mask: Int, + ): Boolean { if (isVoidKeycode(keycode)) return false Timber.d("processKey: keyCode=$keycode, mask=$mask") return processRimeKey(keycode, mask).also { @@ -250,7 +254,10 @@ class Rime(fullCheck: Boolean) { } @JvmStatic - fun setOption(option: String, value: Boolean) { + fun setOption( + option: String, + value: Boolean, + ) { if (isHandlingRimeNotification) return setRimeOption(option, value) } @@ -306,7 +313,10 @@ class Rime(fullCheck: Boolean) { // input @JvmStatic - external fun processRimeKey(keycode: Int, mask: Int): Boolean + external fun processRimeKey( + keycode: Int, + mask: Int, + ): Boolean @JvmStatic external fun commitRimeComposition(): Boolean @@ -326,7 +336,10 @@ class Rime(fullCheck: Boolean) { // runtime options @JvmStatic - external fun setRimeOption(option: String, value: Boolean) + external fun setRimeOption( + option: String, + value: Boolean, + ) @JvmStatic external fun getRimeOption(option: String): Boolean @@ -407,7 +420,10 @@ class Rime(fullCheck: Boolean) { external fun selectRimeSchemas(schemaIds: Array): Boolean @JvmStatic - external fun getRimeStateLabel(optionName: String, state: Boolean): String? + external fun getRimeStateLabel( + optionName: String, + state: Boolean, + ): String? /** call from rime_jni */ @JvmStatic diff --git a/app/src/main/java/com/osfans/trime/core/RimeNotification.kt b/app/src/main/java/com/osfans/trime/core/RimeNotification.kt index 5ab233840d..c5c4644c0a 100644 --- a/app/src/main/java/com/osfans/trime/core/RimeNotification.kt +++ b/app/src/main/java/com/osfans/trime/core/RimeNotification.kt @@ -1,7 +1,6 @@ package com.osfans.trime.core sealed class RimeNotification { - abstract val messageType: MessageType data class SchemaNotification(val messageValue: String) : @@ -51,12 +50,14 @@ sealed class RimeNotification { companion object RimeNotificationHandler { @JvmStatic - fun create(type: String, value: String) = - when (type) { - "schema" -> SchemaNotification(value) - "option" -> OptionNotification(value) - "deploy" -> DeployNotification(value) - else -> UnknownNotification(value) - } + fun create( + type: String, + value: String, + ) = when (type) { + "schema" -> SchemaNotification(value) + "option" -> OptionNotification(value) + "deploy" -> DeployNotification(value) + else -> UnknownNotification(value) + } } } diff --git a/app/src/main/java/com/osfans/trime/data/AppPrefs.kt b/app/src/main/java/com/osfans/trime/data/AppPrefs.kt index 9a9f8476e8..1ff5bc3796 100644 --- a/app/src/main/java/com/osfans/trime/data/AppPrefs.kt +++ b/app/src/main/java/com/osfans/trime/data/AppPrefs.kt @@ -31,7 +31,10 @@ class AppPrefs( * The type is automatically derived from the given [default] value. * @return The value for [key] or [default]. */ - private inline fun getPref(key: String, default: T): T { + private inline fun getPref( + key: String, + default: T, + ): T { return when { false is T -> { shared.getBoolean(key, default as Boolean) as T @@ -53,7 +56,10 @@ class AppPrefs( * Sets the [value] for [key] in the shared preferences, puts the value into the corresponding * cache and returns it. */ - private inline fun setPref(key: String, value: T) { + private inline fun setPref( + key: String, + value: T, + ) { when { false is T -> { shared.edit().putBoolean(key, value as Boolean).apply() @@ -114,6 +120,7 @@ class AppPrefs( const val PID = "general__pid" const val LAST_BUILD_GIT_HASH = "general__last_build_git_hash" } + var lastVersionName: String get() = prefs.getPref(LAST_VERSION_NAME, "") set(v) = prefs.setPref(LAST_VERSION_NAME, v) @@ -173,6 +180,7 @@ class AppPrefs( const val DELETE_CANDIDATE_TIMEOUT = "keyboard__key_delete_candidate_timeout" const val SHOULD_LONG_CLICK_DELETE_CANDIDATE = "keyboard__long_click_delete_candidate" } + var inlinePreedit: InlineModeType get() = InlineModeType.fromString(prefs.getPref(INLINE_PREEDIT_MODE, "preview")) set(v) = prefs.setPref(INLINE_PREEDIT_MODE, v) @@ -298,6 +306,7 @@ class AppPrefs( const val AUTO_DARK = "theme_auto_dark" const val USE_MINI_KEYBOARD = "theme_use_mini_keyboard" } + var selectedTheme: String get() = prefs.getPref(SELECTED_THEME, "trime") set(v) = prefs.setPref(SELECTED_THEME, v) @@ -326,6 +335,7 @@ class AppPrefs( const val LAST_BACKGROUND_SYNC = "profile_last_background_sync" val EXTERNAL_PATH_PREFIX: String = PathUtils.getExternalStoragePath() } + var sharedDataDir: String get() = prefs.getPref(SHARED_DATA_DIR, "$EXTERNAL_PATH_PREFIX/rime") set(v) = prefs.setPref(SHARED_DATA_DIR, v) @@ -358,6 +368,7 @@ class AppPrefs( const val DRAFT_LIMIT = "clipboard_draft_limit" const val CLIPBOARD_LIMIT = "clipboard_clipboard_limit" } + var clipboardCompareRules: List get() = prefs.getPref(CLIPBOARD_COMPARE_RULES, "").trim().split('\n') set(v) = prefs.setPref(CLIPBOARD_COMPARE_RULES, v.joinToString("\n")) @@ -388,6 +399,7 @@ class AppPrefs( const val SHOW_STATUS_BAR_ICON = "other__show_status_bar_icon" const val DESTROY_ON_QUIT = "other__destroy_on_quit" } + var uiMode: String get() = prefs.getPref(UI_MODE, "auto") set(v) = prefs.setPref(UI_MODE, v) diff --git a/app/src/main/java/com/osfans/trime/data/DataDirectoryChangeListener.kt b/app/src/main/java/com/osfans/trime/data/DataDirectoryChangeListener.kt index 38cf2d29e9..b37ae9dd5b 100644 --- a/app/src/main/java/com/osfans/trime/data/DataDirectoryChangeListener.kt +++ b/app/src/main/java/com/osfans/trime/data/DataDirectoryChangeListener.kt @@ -4,7 +4,6 @@ package com.osfans.trime.data * Do something when data directory change. */ object DataDirectoryChangeListener { - // listener list val directoryChangeListeners = mutableListOf() diff --git a/app/src/main/java/com/osfans/trime/data/DataManager.kt b/app/src/main/java/com/osfans/trime/data/DataManager.kt index a24d3f6f95..484a15fb79 100644 --- a/app/src/main/java/com/osfans/trime/data/DataManager.kt +++ b/app/src/main/java/com/osfans/trime/data/DataManager.kt @@ -27,7 +27,9 @@ object DataManager : DataDirectoryChangeListener.Listener { sealed class Diff { object New : Diff() + object Update : Diff() + object Keep : Diff() } @@ -48,7 +50,10 @@ object DataManager : DataDirectoryChangeListener.Listener { return defaultPath.absolutePath } - private fun diff(old: String, new: String): Diff { + private fun diff( + old: String, + new: String, + ): Diff { return when { old.isBlank() -> Diff.New !new.contentEquals(old) -> Diff.Update @@ -64,14 +69,16 @@ object DataManager : DataDirectoryChangeListener.Listener { diff(oldHash, newHash).run { Timber.d("Diff: $this") when (this) { - is Diff.New -> ResourceUtils.copyFileFromAssets( - "rime", - sharedDataDir.absolutePath, - ) - is Diff.Update -> ResourceUtils.copyFileFromAssets( - "rime", - sharedDataDir.absolutePath, - ) + is Diff.New -> + ResourceUtils.copyFileFromAssets( + "rime", + sharedDataDir.absolutePath, + ) + is Diff.Update -> + ResourceUtils.copyFileFromAssets( + "rime", + sharedDataDir.absolutePath, + ) is Diff.Keep -> {} } } diff --git a/app/src/main/java/com/osfans/trime/data/SymbolHistory.kt b/app/src/main/java/com/osfans/trime/data/SymbolHistory.kt index 105dcdec1f..1e30a5835f 100644 --- a/app/src/main/java/com/osfans/trime/data/SymbolHistory.kt +++ b/app/src/main/java/com/osfans/trime/data/SymbolHistory.kt @@ -5,7 +5,6 @@ import com.osfans.trime.util.appContext class SymbolHistory( val capacity: Int, ) : LinkedHashMap(0, .75f, true) { - companion object { const val FILE_NAME = "symbol_history" } @@ -25,8 +24,7 @@ class SymbolHistory( file.writeText(values.joinToString("\n")) } - override fun removeEldestEntry(eldest: MutableMap.MutableEntry?) = - size > capacity + override fun removeEldestEntry(eldest: MutableMap.MutableEntry?) = size > capacity fun insert(s: String) = put(s, s) diff --git a/app/src/main/java/com/osfans/trime/data/db/ClipboardHelper.kt b/app/src/main/java/com/osfans/trime/data/db/ClipboardHelper.kt index fdc90c058a..b5edbb85f6 100644 --- a/app/src/main/java/com/osfans/trime/data/db/ClipboardHelper.kt +++ b/app/src/main/java/com/osfans/trime/data/db/ClipboardHelper.kt @@ -63,24 +63,28 @@ object ClipboardHelper : fun init(context: Context) { clipboardManager.addPrimaryClipChangedListener(this) - clbDb = Room - .databaseBuilder(context, Database::class.java, "clipboard.db") - .addMigrations(Database.MIGRATION_3_4) - .build() + clbDb = + Room + .databaseBuilder(context, Database::class.java, "clipboard.db") + .addMigrations(Database.MIGRATION_3_4) + .build() clbDao = clbDb.databaseDao() launch { updateItemCount() } } suspend fun get(id: Int) = clbDao.get(id) + suspend fun getAll() = clbDao.getAll() suspend fun pin(id: Int) = clbDao.updatePinned(id, true) + suspend fun unpin(id: Int) = clbDao.updatePinned(id, false) suspend fun delete(id: Int) { clbDao.delete(id) updateItemCount() } + suspend fun deleteAll(skipUnpinned: Boolean = true) { if (skipUnpinned) { clbDao.deleteAllUnpinned() @@ -136,19 +140,23 @@ object ClipboardHelper : private suspend fun removeOutdated() { val all = clbDao.getAll() if (all.size > limit) { - val outdated = all - .map { - if (it.pinned) { - it.copy(id = Int.MAX_VALUE) - } else { - it + val outdated = + all + .map { + if (it.pinned) { + it.copy(id = Int.MAX_VALUE) + } else { + it + } } - } - .sortedBy { it.id } - .subList(0, all.size - limit) + .sortedBy { it.id } + .subList(0, all.size - limit) clbDao.delete(outdated) } } - suspend fun updateText(id: Int, text: String) = clbDao.updateText(id, text) + suspend fun updateText( + id: Int, + text: String, + ) = clbDao.updateText(id, text) } diff --git a/app/src/main/java/com/osfans/trime/data/db/CollectionHelper.kt b/app/src/main/java/com/osfans/trime/data/db/CollectionHelper.kt index 7de9e142a9..aef90bbda1 100644 --- a/app/src/main/java/com/osfans/trime/data/db/CollectionHelper.kt +++ b/app/src/main/java/com/osfans/trime/data/db/CollectionHelper.kt @@ -11,10 +11,11 @@ object CollectionHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dis private lateinit var cltDao: DatabaseDao fun init(context: Context) { - cltDb = Room - .databaseBuilder(context, Database::class.java, "collection.db") - .addMigrations(Database.MIGRATION_3_4) - .build() + cltDb = + Room + .databaseBuilder(context, Database::class.java, "collection.db") + .addMigrations(Database.MIGRATION_3_4) + .build() cltDao = cltDb.databaseDao() } @@ -36,5 +37,8 @@ object CollectionHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dis } } - suspend fun updateText(id: Int, text: String) = cltDao.updateText(id, text) + suspend fun updateText( + id: Int, + text: String, + ) = cltDao.updateText(id, text) } diff --git a/app/src/main/java/com/osfans/trime/data/db/Database.kt b/app/src/main/java/com/osfans/trime/data/db/Database.kt index 1635730201..7273973151 100644 --- a/app/src/main/java/com/osfans/trime/data/db/Database.kt +++ b/app/src/main/java/com/osfans/trime/data/db/Database.kt @@ -12,32 +12,33 @@ abstract class Database : RoomDatabase() { abstract fun databaseDao(): DatabaseDao companion object { - val MIGRATION_3_4 = object : Migration(3, 4) { - override fun migrate(database: SupportSQLiteDatabase) { - if (database.needUpgrade(4)) { - database.execSQL("ALTER TABLE ${DatabaseBean.TABLE_NAME} RENAME TO _t_data") - database.execSQL("ALTER TABLE _t_data ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS ${DatabaseBean.TABLE_NAME} ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - text TEXT, - html TEXT, - type INTEGER NOT NULL, - time INTEGER NOT NULL, - pinned INTEGER NOT NULL + val MIGRATION_3_4 = + object : Migration(3, 4) { + override fun migrate(database: SupportSQLiteDatabase) { + if (database.needUpgrade(4)) { + database.execSQL("ALTER TABLE ${DatabaseBean.TABLE_NAME} RENAME TO _t_data") + database.execSQL("ALTER TABLE _t_data ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS ${DatabaseBean.TABLE_NAME} ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + text TEXT, + html TEXT, + type INTEGER NOT NULL, + time INTEGER NOT NULL, + pinned INTEGER NOT NULL + ) + """.trimIndent(), ) - """.trimIndent(), - ) - database.execSQL( - """ - INSERT INTO ${DatabaseBean.TABLE_NAME} (id, text, html, type, time, pinned) - SELECT id, text, html, type, time, pinned FROM _t_data - """.trimIndent(), - ) - database.execSQL("DROP TABLE _t_data") + database.execSQL( + """ + INSERT INTO ${DatabaseBean.TABLE_NAME} (id, text, html, type, time, pinned) + SELECT id, text, html, type, time, pinned FROM _t_data + """.trimIndent(), + ) + database.execSQL("DROP TABLE _t_data") + } } } - } } } diff --git a/app/src/main/java/com/osfans/trime/data/db/DatabaseBean.kt b/app/src/main/java/com/osfans/trime/data/db/DatabaseBean.kt index e8a0d86302..7a404f8a84 100644 --- a/app/src/main/java/com/osfans/trime/data/db/DatabaseBean.kt +++ b/app/src/main/java/com/osfans/trime/data/db/DatabaseBean.kt @@ -32,7 +32,8 @@ data class DatabaseBean( } enum class BeanType { - TEXT, HTML + TEXT, + HTML, } class Converters { diff --git a/app/src/main/java/com/osfans/trime/data/db/DatabaseDao.kt b/app/src/main/java/com/osfans/trime/data/db/DatabaseDao.kt index c28436dd9f..b6c2eb3eda 100644 --- a/app/src/main/java/com/osfans/trime/data/db/DatabaseDao.kt +++ b/app/src/main/java/com/osfans/trime/data/db/DatabaseDao.kt @@ -15,10 +15,16 @@ interface DatabaseDao { suspend fun update(bean: DatabaseBean) @Query("UPDATE ${DatabaseBean.TABLE_NAME} SET text=:newText WHERE id=:id") - suspend fun updateText(id: Int, newText: String) + suspend fun updateText( + id: Int, + newText: String, + ) @Query("UPDATE ${DatabaseBean.TABLE_NAME} SET pinned=:pinned WHERE id=:id") - suspend fun updatePinned(id: Int, pinned: Boolean) + suspend fun updatePinned( + id: Int, + pinned: Boolean, + ) @Delete suspend fun delete(bean: DatabaseBean) diff --git a/app/src/main/java/com/osfans/trime/data/db/DraftHelper.kt b/app/src/main/java/com/osfans/trime/data/db/DraftHelper.kt index 5cf271ce15..99f7397a24 100644 --- a/app/src/main/java/com/osfans/trime/data/db/DraftHelper.kt +++ b/app/src/main/java/com/osfans/trime/data/db/DraftHelper.kt @@ -32,26 +32,33 @@ object DraftHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatch var lastBean: DatabaseBean? = null fun init(context: Context) { - dftDb = Room - .databaseBuilder(context, Database::class.java, "draft.db") - .addMigrations(Database.MIGRATION_3_4) - .build() + dftDb = + Room + .databaseBuilder(context, Database::class.java, "draft.db") + .addMigrations(Database.MIGRATION_3_4) + .build() dftDao = dftDb.databaseDao() launch { updateItemCount() } } suspend fun get(id: Int) = dftDao.get(id) + suspend fun getAll() = dftDao.getAll() suspend fun pin(id: Int) = dftDao.updatePinned(id, true) + suspend fun unpin(id: Int) = dftDao.updatePinned(id, false) - suspend fun updateText(id: Int, text: String) = dftDao.updateText(id, text) + suspend fun updateText( + id: Int, + text: String, + ) = dftDao.updateText(id, text) suspend fun delete(id: Int) { dftDao.delete(id) updateItemCount() } + suspend fun deleteAll(skipUnpinned: Boolean = true) { if (skipUnpinned) { dftDao.deleteAllUnpinned() @@ -92,16 +99,17 @@ object DraftHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatch private suspend fun removeOutdated() { val all = dftDao.getAll() if (all.size > limit) { - val outdated = all - .map { - if (it.pinned) { - it.copy(id = Int.MAX_VALUE) - } else { - it + val outdated = + all + .map { + if (it.pinned) { + it.copy(id = Int.MAX_VALUE) + } else { + it + } } - } - .sortedBy { it.id } - .subList(0, all.size - limit) + .sortedBy { it.id } + .subList(0, all.size - limit) dftDao.delete(outdated) } } diff --git a/app/src/main/java/com/osfans/trime/data/opencc/OpenCCDictManager.kt b/app/src/main/java/com/osfans/trime/data/opencc/OpenCCDictManager.kt index 3ed68bca61..4c1c934ac9 100644 --- a/app/src/main/java/com/osfans/trime/data/opencc/OpenCCDictManager.kt +++ b/app/src/main/java/com/osfans/trime/data/opencc/OpenCCDictManager.kt @@ -29,13 +29,15 @@ object OpenCCDictManager : DataDirectoryChangeListener.Listener { userDir = File(DataManager.userDataDir, "opencc").also { it.mkdirs() } } - fun sharedDictionaries(): List = sharedDir - .listFiles() - ?.mapNotNull { Dictionary.new(it) } ?: listOf() + fun sharedDictionaries(): List = + sharedDir + .listFiles() + ?.mapNotNull { Dictionary.new(it) } ?: listOf() - fun userDictionaries(): List = userDir - .listFiles() - ?.mapNotNull { Dictionary.new(it) } ?: listOf() + fun userDictionaries(): List = + userDir + .listFiles() + ?.mapNotNull { Dictionary.new(it) } ?: listOf() fun getAllDictionaries(): List = if (sharedDir.path == userDir.path) { @@ -44,20 +46,21 @@ object OpenCCDictManager : DataDirectoryChangeListener.Listener { (sharedDictionaries() + userDictionaries()) } - fun openCCDictionaries(): List = - getAllDictionaries().mapNotNull { it as? OpenCCDictionary } + fun openCCDictionaries(): List = getAllDictionaries().mapNotNull { it as? OpenCCDictionary } fun importFromFile(file: File): OpenCCDictionary { - val raw = Dictionary.new(file) - ?: throw IllegalArgumentException("${file.path} is not a opencc/text dictionary") + val raw = + Dictionary.new(file) + ?: throw IllegalArgumentException("${file.path} is not a opencc/text dictionary") // convert to opencc format in dictionaries dir // preserve original file name - val new = raw.toOpenCCDictionary( - File( - userDir, - file.nameWithoutExtension + ".${Dictionary.Type.OPENCC.ext}", - ), - ) + val new = + raw.toOpenCCDictionary( + File( + userDir, + file.nameWithoutExtension + ".${Dictionary.Type.OPENCC.ext}", + ), + ) Timber.d("Converted $raw to $new") return new } @@ -77,7 +80,10 @@ object OpenCCDictManager : DataDirectoryChangeListener.Listener { } } - fun importFromInputStream(stream: InputStream, name: String): OpenCCDictionary { + fun importFromInputStream( + stream: InputStream, + name: String, + ): OpenCCDictionary { val tempFile = File(appContext.cacheDir, name) tempFile.outputStream().use { stream.copyTo(it) @@ -88,7 +94,10 @@ object OpenCCDictManager : DataDirectoryChangeListener.Listener { } @JvmStatic - fun convertLine(input: String, configFileName: String): String { + fun convertLine( + input: String, + configFileName: String, + ): String { if (configFileName.isEmpty()) return input with(File(userDir, configFileName)) { if (exists()) return openCCLineConv(input, path) @@ -101,10 +110,17 @@ object OpenCCDictManager : DataDirectoryChangeListener.Listener { } @JvmStatic - external fun openCCDictConv(src: String, dest: String, mode: Boolean) + external fun openCCDictConv( + src: String, + dest: String, + mode: Boolean, + ) @JvmStatic - external fun openCCLineConv(input: String, configFileName: String): String + external fun openCCLineConv( + input: String, + configFileName: String, + ): String @JvmStatic external fun getOpenCCVersion(): String diff --git a/app/src/main/java/com/osfans/trime/data/opencc/dict/Dictionary.kt b/app/src/main/java/com/osfans/trime/data/opencc/dict/Dictionary.kt index 9d252a29d3..2708f1df5a 100644 --- a/app/src/main/java/com/osfans/trime/data/opencc/dict/Dictionary.kt +++ b/app/src/main/java/com/osfans/trime/data/opencc/dict/Dictionary.kt @@ -4,7 +4,9 @@ import java.io.File abstract class Dictionary { enum class Type(val ext: String) { - OPENCC("ocd2"), Text("txt"); + OPENCC("ocd2"), + Text("txt"), + ; companion object { fun fromFileName(name: String): Type? { @@ -62,10 +64,11 @@ abstract class Dictionary { override fun toString(): String = "${javaClass.simpleName}[$name -> ${file.path}]" companion object { - fun new(it: File): Dictionary? = when (Type.fromFileName(it.name)) { - Type.OPENCC -> OpenCCDictionary(it) - Type.Text -> TextDictionary(it) - null -> null - } + fun new(it: File): Dictionary? = + when (Type.fromFileName(it.name)) { + Type.OPENCC -> OpenCCDictionary(it) + Type.Text -> TextDictionary(it) + null -> null + } } } diff --git a/app/src/main/java/com/osfans/trime/data/opencc/dict/OpenCCDictionary.kt b/app/src/main/java/com/osfans/trime/data/opencc/dict/OpenCCDictionary.kt index 38afae97f5..90b55546bd 100644 --- a/app/src/main/java/com/osfans/trime/data/opencc/dict/OpenCCDictionary.kt +++ b/app/src/main/java/com/osfans/trime/data/opencc/dict/OpenCCDictionary.kt @@ -4,7 +4,6 @@ import com.osfans.trime.data.opencc.OpenCCDictManager import java.io.File class OpenCCDictionary(file: File) : Dictionary() { - override var file: File = file private set @@ -14,23 +13,25 @@ class OpenCCDictionary(file: File) : Dictionary() { override val type: Type = Type.OPENCC override val name: String - get() = if (isOCD2) { - super.name - } else { - file.name.substringBefore("") - } + get() = + if (isOCD2) { + super.name + } else { + file.name.substringBefore("") + } init { ensureFileExists() - isOCD2 = when { - file.extension == type.ext -> { - true + isOCD2 = + when { + file.extension == type.ext -> { + true + } + file.name.endsWith(".$OLD_FORMAT") -> { + false + } + else -> throw IllegalArgumentException("Not a libime dict ${file.name}") } - file.name.endsWith(".$OLD_FORMAT") -> { - false - } - else -> throw IllegalArgumentException("Not a libime dict ${file.name}") - } } fun useOCD2() { diff --git a/app/src/main/java/com/osfans/trime/data/schema/Schema.kt b/app/src/main/java/com/osfans/trime/data/schema/Schema.kt index 788f562f53..edfde4406c 100644 --- a/app/src/main/java/com/osfans/trime/data/schema/Schema.kt +++ b/app/src/main/java/com/osfans/trime/data/schema/Schema.kt @@ -8,17 +8,20 @@ import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer class Schema(schemaId: String = ".default") { - private val config = if (schemaId == ".default") { - Config.create("default") - } else { - Config.create("$schemaId.schema") - } + private val config = + if (schemaId == ".default") { + Config.create("default") + } else { + Config.create("$schemaId.schema") + } - val switches get() = config?.getList("switches") - ?.decode(ListSerializer(Switch.serializer())) + val switches get() = + config?.getList("switches") + ?.decode(ListSerializer(Switch.serializer())) - val symbols get() = config?.getMap("punctuator/symbols") - ?.decode(MapSerializer(String.serializer(), ListSerializer(String.serializer()))) + val symbols get() = + config?.getMap("punctuator/symbols") + ?.decode(MapSerializer(String.serializer(), ListSerializer(String.serializer()))) val alphabet get() = config?.getString("speller/alphabet") diff --git a/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt b/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt index ff9301fa07..17a2a30ab3 100644 --- a/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt +++ b/app/src/main/java/com/osfans/trime/data/schema/SchemaManager.kt @@ -27,14 +27,15 @@ object SchemaManager { fun updateSwitchOptions() { if (!this::visibleSwitches.isInitialized || visibleSwitches.isEmpty()) return // 無方案 visibleSwitches.forEach { s -> - s.enabled = if (s.options.isNullOrEmpty()) { // 只有单 Rime 运行时选项的开关,开关名即选项名,标记其启用状态 - Rime.getRimeOption(s.name!!).compareTo(false) - } else { // 带有一系列 Rime 运行时选项的开关,找到启用的选项并标记 - // 将启用状态标记为此选项的索引值,方便切换时直接从选项列表中获取 - // 注意:有可能每个 option 的状态都为 false(未启用), 因此 indexOfFirst 可能会返回 -1, - // 需要 coerceAtLeast 确保其至少为 0 - s.options.indexOfFirst { Rime.getRimeOption(it) }.coerceAtLeast(0) - } + s.enabled = + if (s.options.isNullOrEmpty()) { // 只有单 Rime 运行时选项的开关,开关名即选项名,标记其启用状态 + Rime.getRimeOption(s.name!!).compareTo(false) + } else { // 带有一系列 Rime 运行时选项的开关,找到启用的选项并标记 + // 将启用状态标记为此选项的索引值,方便切换时直接从选项列表中获取 + // 注意:有可能每个 option 的状态都为 false(未启用), 因此 indexOfFirst 可能会返回 -1, + // 需要 coerceAtLeast 确保其至少为 0 + s.options.indexOfFirst { Rime.getRimeOption(it) }.coerceAtLeast(0) + } } } @@ -43,15 +44,16 @@ object SchemaManager { if (!this::visibleSwitches.isInitialized || visibleSwitches.isEmpty()) return val switch = visibleSwitches[index] val enabled = switch.enabled - switch.enabled = if (switch.options.isNullOrEmpty()) { - (1 - enabled).also { Rime.setOption(switch.name!!, it == 1) } - } else { - val options = switch.options - ((enabled + 1) % options.size).also { - Rime.setOption(options[enabled], false) - Rime.setOption(options[it], true) + switch.enabled = + if (switch.options.isNullOrEmpty()) { + (1 - enabled).also { Rime.setOption(switch.name!!, it == 1) } + } else { + val options = switch.options + ((enabled + 1) % options.size).also { + Rime.setOption(options[enabled], false) + Rime.setOption(options[it], true) + } } - } } @JvmStatic @@ -61,11 +63,12 @@ object SchemaManager { val switch = visibleSwitches[it] val enabled = switch.enabled val text = switch.states!![enabled] - val comment = if (switch.options.isNullOrEmpty()) { - "${if (arrow) "→ " else ""}${switch.states[1 - enabled]}" - } else { - "" - } + val comment = + if (switch.options.isNullOrEmpty()) { + "${if (arrow) "→ " else ""}${switch.states[1 - enabled]}" + } else { + "" + } CandidateListItem(comment, text) } } diff --git a/app/src/main/java/com/osfans/trime/data/sound/SoundThemeManager.kt b/app/src/main/java/com/osfans/trime/data/sound/SoundThemeManager.kt index e59c00fab2..99c63d9071 100644 --- a/app/src/main/java/com/osfans/trime/data/sound/SoundThemeManager.kt +++ b/app/src/main/java/com/osfans/trime/data/sound/SoundThemeManager.kt @@ -9,7 +9,6 @@ import timber.log.Timber import java.io.File object SoundThemeManager : DataDirectoryChangeListener.Listener { - var userDir = File(DataManager.userDataDir, "sound") init { @@ -24,24 +23,27 @@ object SoundThemeManager : DataDirectoryChangeListener.Listener { userDir = File(DataManager.userDataDir, "sound") } - private val yaml = Yaml( - configuration = YamlConfiguration( - strictMode = false, - ), - ) + private val yaml = + Yaml( + configuration = + YamlConfiguration( + strictMode = false, + ), + ) private fun listSounds(): MutableList { val files = userDir.listFiles { f -> f.name.endsWith("sound.yaml") } return files ?.mapNotNull decode@{ f -> - val theme = runCatching { - yaml.decodeFromString(SoundTheme.serializer(), f.readText()).also { - it.name = f.name.substringBefore('.') + val theme = + runCatching { + yaml.decodeFromString(SoundTheme.serializer(), f.readText()).also { + it.name = f.name.substringBefore('.') + } + }.getOrElse { e -> + Timber.w("Failed to decode sound theme file ${f.absolutePath}: ${e.message}") + return@decode null } - }.getOrElse { e -> - Timber.w("Failed to decode sound theme file ${f.absolutePath}: ${e.message}") - return@decode null - } return@decode theme } ?.toMutableList() ?: mutableListOf() @@ -71,7 +73,8 @@ object SoundThemeManager : DataDirectoryChangeListener.Listener { fun getActiveSoundTheme() = runCatching { currentSoundTheme } - fun getActiveSoundFilePaths() = runCatching { - currentSoundTheme.let { t -> t.sound.map { "${userDir.path}/${t.folder}/$it" } } - } + fun getActiveSoundFilePaths() = + runCatching { + currentSoundTheme.let { t -> t.sound.map { "${userDir.path}/${t.folder}/$it" } } + } } 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 5ab6958dbc..591d2b04de 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 @@ -71,7 +71,7 @@ class Theme { return self as Theme } - private const val defaultThemeName = "trime" + private val defaultThemeName = "trime" } init { @@ -168,7 +168,10 @@ class Theme { return if (o is Int) o else null } - fun getColor(m: Map, key: String?): Int? { + fun getColor( + m: Map, + key: String?, + ): Int? { if (m[key] == null) return null return ColorUtils.parseColor(m[key] as String?) ?: getColor(m[key] as String?) } @@ -186,7 +189,10 @@ class Theme { } // API 2.0 - fun getDrawable(m: Map, key: String): Drawable? { + fun getDrawable( + m: Map, + key: String, + ): Drawable? { m[key] ?: return null val value = m[key] as String? val override = ColorUtils.parseColor(value) @@ -241,33 +247,35 @@ class Theme { } fun remapKeyboardId(name: String): String { - val remapped = if (".default" == name) { - val currentSchemaId = Rime.getCurrentRimeSchema() - val shortSchemaId = currentSchemaId.split("_".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0] - if (theme.presetKeyboards!!.containsKey(shortSchemaId)) { - return shortSchemaId - } else { - val alphabet = SchemaManager.getActiveSchema().alphabet - val twentySix = "qwerty" - if (!alphabet.isNullOrEmpty() && theme.presetKeyboards!!.containsKey(alphabet)) { - return alphabet + val remapped = + if (".default" == name) { + val currentSchemaId = Rime.getCurrentRimeSchema() + val shortSchemaId = currentSchemaId.split("_".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0] + if (theme.presetKeyboards!!.containsKey(shortSchemaId)) { + return shortSchemaId } else { - if (!alphabet.isNullOrEmpty() && (alphabet.contains(",") || alphabet.contains(";"))) { - twentySix + "_" - } else if (!alphabet.isNullOrEmpty() && (alphabet.contains("0") || alphabet.contains("1"))) { - twentySix + "0" + val alphabet = SchemaManager.getActiveSchema().alphabet + val twentySix = "qwerty" + if (!alphabet.isNullOrEmpty() && theme.presetKeyboards!!.containsKey(alphabet)) { + return alphabet } else { - twentySix + if (!alphabet.isNullOrEmpty() && (alphabet.contains(",") || alphabet.contains(";"))) { + twentySix + "_" + } else if (!alphabet.isNullOrEmpty() && (alphabet.contains("0") || alphabet.contains("1"))) { + twentySix + "0" + } else { + twentySix + } } } + } else { + name } - } 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!") + val defaultMap = + theme.presetKeyboards!!["default"] as Map? + ?: throw IllegalStateException("The default keyboard definition is missing!") if (defaultMap.containsKey("import_preset")) { return defaultMap["import_preset"] as? String ?: "default" } @@ -289,7 +297,11 @@ class Theme { } private var oneHandMode = 0 - fun getKeyboardPadding(oneHandMode1: Int, landMode: Boolean): IntArray { + + fun getKeyboardPadding( + oneHandMode1: Int, + landMode: Boolean, + ): IntArray { keyboardPadding = IntArray(3) this.oneHandMode = oneHandMode1 if (landMode) { diff --git a/app/src/main/java/com/osfans/trime/data/theme/ThemeManager.kt b/app/src/main/java/com/osfans/trime/data/theme/ThemeManager.kt index 09e7091291..4889b54c7f 100644 --- a/app/src/main/java/com/osfans/trime/data/theme/ThemeManager.kt +++ b/app/src/main/java/com/osfans/trime/data/theme/ThemeManager.kt @@ -6,7 +6,6 @@ import com.osfans.trime.data.DataManager import java.io.File object ThemeManager : DataDirectoryChangeListener.Listener { - init { // register listener DataDirectoryChangeListener.addDirectoryChangeListener(this) diff --git a/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt b/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt index cce5ea2506..c3315255f5 100644 --- a/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt +++ b/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt @@ -44,53 +44,61 @@ import java.util.concurrent.TimeUnit /** 接收 Intent 廣播事件 */ class IntentReceiver : BroadcastReceiver(), CoroutineScope by MainScope() { - override fun onReceive(context: Context, intent: Intent) { + override fun onReceive( + context: Context, + intent: Intent, + ) { val command = intent.action ?: return Timber.d("Received Command = %s", command) when (command) { - COMMAND_DEPLOY -> launch { - withContext(Dispatchers.Default) { + COMMAND_DEPLOY -> + launch { + withContext(Dispatchers.Default) { + Rime.deploy() + } + ToastUtils.showLong(R.string.deploy_finish) + } + COMMAND_SYNC -> + async { + Rime.syncRimeUserData() Rime.deploy() } - ToastUtils.showLong(R.string.deploy_finish) - } - COMMAND_SYNC -> async { - Rime.syncRimeUserData() - Rime.deploy() - } - COMMAND_TIMING_SYNC -> async { - // 获取唤醒锁 - val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager - val wakeLock = powerManager.newWakeLock(PARTIAL_WAKE_LOCK, "com.osfans.trime:WakeLock") - wakeLock.acquire(600000) // 10分钟超时 - val cal = Calendar.getInstance() - val triggerTime = cal.timeInMillis + TimeUnit.DAYS.toMillis(1) // 下次同步时间 - AppPrefs.defaultInstance().profile.timingSyncTriggerTime = triggerTime // 更新定时同步偏好值 - val alarmManager = - context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val pendingIntent = PendingIntent.getBroadcast( // 设置待发送的同步事件 - context, - 0, - Intent("com.osfans.trime.timing.sync"), - if (VERSION.SDK_INT >= VERSION_CODES.M) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + COMMAND_TIMING_SYNC -> + async { + // 获取唤醒锁 + val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager + val wakeLock = powerManager.newWakeLock(PARTIAL_WAKE_LOCK, "com.osfans.trime:WakeLock") + wakeLock.acquire(600000) // 10分钟超时 + val cal = Calendar.getInstance() + val triggerTime = cal.timeInMillis + TimeUnit.DAYS.toMillis(1) // 下次同步时间 + AppPrefs.defaultInstance().profile.timingSyncTriggerTime = triggerTime // 更新定时同步偏好值 + val alarmManager = + context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + // 设置待发送的同步事件 + val pendingIntent = + PendingIntent.getBroadcast( + context, + 0, + Intent("com.osfans.trime.timing.sync"), + if (VERSION.SDK_INT >= VERSION_CODES.M) { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + } else { + PendingIntent.FLAG_UPDATE_CURRENT + }, + ) + if (VERSION.SDK_INT >= VERSION_CODES.M) { // 根据SDK设置alarm任务 + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, + triggerTime, + pendingIntent, + ) } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) - if (VERSION.SDK_INT >= VERSION_CODES.M) { // 根据SDK设置alarm任务 - alarmManager.setExactAndAllowWhileIdle( - AlarmManager.RTC_WAKEUP, - triggerTime, - pendingIntent, - ) - } else { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent) + alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent) + } + Rime.syncRimeUserData() + Rime.deploy() + wakeLock.release() // 释放唤醒锁 } - Rime.syncRimeUserData() - Rime.deploy() - wakeLock.release() // 释放唤醒锁 - } else -> return } } diff --git a/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt b/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt index 008562ec3a..ee8d677b0b 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt @@ -16,7 +16,6 @@ import com.osfans.trime.ime.text.TextInputManager import timber.log.Timber class EditorInstance(private val ims: InputMethodService) { - val prefs get() = AppPrefs.defaultInstance() val inputConnection: InputConnection? get() = ims.currentInputConnection @@ -38,7 +37,10 @@ class EditorInstance(private val ims: InputMethodService) { var lastCommittedText: CharSequence = "" // 直接commit不做任何处理 - fun commitText(text: CharSequence, clearMeatKeyState: Boolean = false): Boolean { + fun commitText( + text: CharSequence, + clearMeatKeyState: Boolean = false, + ): Boolean { val ic = inputConnection ?: return false ic.commitText(text, 1) if (text.isNotEmpty()) { @@ -64,12 +66,13 @@ class EditorInstance(private val ims: InputMethodService) { fun updateComposingText() { val ic = inputConnection ?: return - val composingText = when (prefs.keyboard.inlinePreedit) { - InlineModeType.INLINE_PREVIEW -> Rime.composingText - InlineModeType.INLINE_COMPOSITION -> Rime.compositionText - InlineModeType.INLINE_INPUT -> Rime.getRimeRawInput() ?: "" - else -> "" - } + val composingText = + when (prefs.keyboard.inlinePreedit) { + InlineModeType.INLINE_PREVIEW -> Rime.composingText + InlineModeType.INLINE_COMPOSITION -> Rime.compositionText + InlineModeType.INLINE_INPUT -> Rime.getRimeRawInput() ?: "" + else -> "" + } if (ic.getSelectedText(0).isNullOrEmpty() || composingText.isNotEmpty()) { ic.setComposingText(composingText, 1) } @@ -167,7 +170,11 @@ class EditorInstance(private val ims: InputMethodService) { return metaState } - private fun sendDownKeyEvent(eventTime: Long, keyEventCode: Int, metaState: Int): Boolean { + private fun sendDownKeyEvent( + eventTime: Long, + keyEventCode: Int, + metaState: Int, + ): Boolean { val ic = inputConnection ?: return false return ic.sendKeyEvent( KeyEvent( @@ -185,7 +192,11 @@ class EditorInstance(private val ims: InputMethodService) { ) } - private fun sendUpKeyEvent(eventTime: Long, keyEventCode: Int, metaState: Int): Boolean { + private fun sendUpKeyEvent( + eventTime: Long, + keyEventCode: Int, + metaState: Int, + ): Boolean { val ic = inputConnection ?: return false return ic.sendKeyEvent( KeyEvent( @@ -213,7 +224,11 @@ class EditorInstance(private val ims: InputMethodService) { * * @return True on success, false if an error occurred or the input connection is invalid. */ - fun sendDownUpKeyEvent(keyEventCode: Int, metaState: Int = meta(), count: Int = 1): Boolean { + fun sendDownUpKeyEvent( + keyEventCode: Int, + metaState: Int = meta(), + count: Int = 1, + ): Boolean { if (count < 1) return false val ic = inputConnection ?: return false ic.clearMetaKeyStates( diff --git a/app/src/main/java/com/osfans/trime/ime/enums/InlineModeType.kt b/app/src/main/java/com/osfans/trime/ime/enums/InlineModeType.kt index 54b7130e6b..62e5e2c427 100644 --- a/app/src/main/java/com/osfans/trime/ime/enums/InlineModeType.kt +++ b/app/src/main/java/com/osfans/trime/ime/enums/InlineModeType.kt @@ -19,7 +19,11 @@ package com.osfans.trime.ime.enums /** 嵌入模式枚举 */ enum class InlineModeType { - INLINE_NONE, INLINE_PREVIEW, INLINE_COMPOSITION, INLINE_INPUT; + INLINE_NONE, + INLINE_PREVIEW, + INLINE_COMPOSITION, + INLINE_INPUT, + ; companion object { fun fromString(string: String): InlineModeType { diff --git a/app/src/main/java/com/osfans/trime/ime/enums/KeyCommandType.kt b/app/src/main/java/com/osfans/trime/ime/enums/KeyCommandType.kt index 18d1b67830..470af181f3 100644 --- a/app/src/main/java/com/osfans/trime/ime/enums/KeyCommandType.kt +++ b/app/src/main/java/com/osfans/trime/ime/enums/KeyCommandType.kt @@ -17,9 +17,17 @@ */ package com.osfans.trime.ime.enums -/*按键的特殊命令枚举(仅用于新增的liquidKeyboard)*/ +// 按键的特殊命令枚举(仅用于新增的liquidKeyboard) enum class KeyCommandType { - NULL, LEFT, RIGHT, EXIT, DEL_LEFT, DEL_RIGHT, UNDO, REDO; + NULL, + LEFT, + RIGHT, + EXIT, + DEL_LEFT, + DEL_RIGHT, + UNDO, + REDO, + ; companion object { @JvmStatic diff --git a/app/src/main/java/com/osfans/trime/ime/enums/KeyEventType.kt b/app/src/main/java/com/osfans/trime/ime/enums/KeyEventType.kt index 4173a77566..349e8457f7 100644 --- a/app/src/main/java/com/osfans/trime/ime/enums/KeyEventType.kt +++ b/app/src/main/java/com/osfans/trime/ime/enums/KeyEventType.kt @@ -2,7 +2,17 @@ package com.osfans.trime.ime.enums /** 按键事件枚举 */ enum class KeyEventType { - // 长按按键展开列表时,正上方为长按对应按键,排序如上,不展示combo及之前的按键,展示extra - COMPOSING, HAS_MENU, PAGING, COMBO, ASCII, CLICK, SWIPE_UP, LONG_CLICK, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT, EXTRA + COMPOSING, + HAS_MENU, + PAGING, + COMBO, + ASCII, + CLICK, + SWIPE_UP, + LONG_CLICK, + SWIPE_DOWN, + SWIPE_LEFT, + SWIPE_RIGHT, + EXTRA, } diff --git a/app/src/main/java/com/osfans/trime/ime/enums/Keycode.kt b/app/src/main/java/com/osfans/trime/ime/enums/Keycode.kt index 5b59b16ccf..b2f45eada9 100644 --- a/app/src/main/java/com/osfans/trime/ime/enums/Keycode.kt +++ b/app/src/main/java/com/osfans/trime/ime/enums/Keycode.kt @@ -13,56 +13,339 @@ enum class Keycode { // // 符号英文名称-图形对应关系可以参考 https://www.mianfeiziti.com/font_glyph-172795.htm - VoidSymbol, SOFT_LEFT, SOFT_RIGHT, HOME, BACK, CALL, ENDCALL, - _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, - asterisk, numbersign, Up, Down, Left, Right, KP_Begin, - VOLUME_UP, VOLUME_DOWN, POWER, CAMERA, Clear, - a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, - comma, period, Alt_L, Alt_R, Shift_L, Shift_R, Tab, space, - SYM, EXPLORER, ENVELOPE, Return, BackSpace, - grave, minus, equal, bracketleft, bracketright, backslash, semicolon, apostrophe, slash, at, - NUM, HEADSETHOOK, FOCUS, plus, Menu, NOTIFICATION, Find, - MEDIA_PLAY_PAUSE, MEDIA_STOP, MEDIA_NEXT, MEDIA_PREVIOUS, MEDIA_REWIND, MEDIA_FAST_FORWARD, MUTE, - Page_Up, Page_Down, PICTSYMBOLS, Mode_switch, - BUTTON_A, BUTTON_B, BUTTON_C, BUTTON_X, BUTTON_Y, BUTTON_Z, - BUTTON_L1, BUTTON_R1, BUTTON_L2, BUTTON_R2, - BUTTON_THUMBL, BUTTON_THUMBR, BUTTON_START, BUTTON_SELECT, BUTTON_MODE, - Escape, Delete, Control_L, Control_R, Caps_Lock, Scroll_Lock, Meta_L, Meta_R, - function, Sys_Req, Pause, Home, End, Insert, Next, - MEDIA_PLAY, MEDIA_PAUSE, MEDIA_CLOSE, MEDIA_EJECT, MEDIA_RECORD, - F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, - Num_Lock, KP_0, KP_1, KP_2, KP_3, KP_4, KP_5, KP_6, KP_7, KP_8, KP_9, - KP_Divide, KP_Multiply, KP_Subtract, KP_Add, KP_Decimal, KP_Separator, KP_Enter, KP_Equal, - parenleft, parenright, - VOLUME_MUTE, INFO, CHANNEL_UP, CHANNEL_DOWN, ZOOM_IN, ZOOM_OUT, - TV, WINDOW, GUIDE, DVR, BOOKMARK, CAPTIONS, SETTINGS, - TV_POWER, TV_INPUT, STB_POWER, STB_INPUT, AVR_POWER, AVR_INPUT, - PROG_RED, PROG_GREEN, PROG_YELLOW, PROG_BLUE, APP_SWITCH, - BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4, BUTTON_5, BUTTON_6, BUTTON_7, BUTTON_8, - BUTTON_9, BUTTON_10, BUTTON_11, BUTTON_12, BUTTON_13, BUTTON_14, BUTTON_15, BUTTON_16, - LANGUAGE_SWITCH, MANNER_MODE, _3D_MODE, CONTACTS, CALENDAR, MUSIC, CALCULATOR, - Zenkaku_Hankaku, Eisu_toggle, Muhenkan, Henkan, Hiragana_Katakana, yen, RO, Kana_Lock, - ASSIST, BRIGHTNESS_DOWN, BRIGHTNESS_UP, MEDIA_AUDIO_TRACK, - SLEEP, WAKEUP, PAIRING, MEDIA_TOP_MENU, _11, _12, LAST_CHANNEL, TV_DATA_SERVICE, VOICE_ASSIST, - TV_RADIO_SERVICE, TV_TELETEXT, TV_NUMBER_ENTRY, TV_TERRESTRIAL_ANALOG, TV_TERRESTRIAL_DIGITAL, - TV_SATELLITE, TV_SATELLITE_BS, TV_SATELLITE_CS, TV_SATELLITE_SERVICE, TV_NETWORK, TV_ANTENNA_CABLE, - TV_INPUT_HDMI_1, TV_INPUT_HDMI_2, TV_INPUT_HDMI_3, TV_INPUT_HDMI_4, - TV_INPUT_COMPOSITE_1, TV_INPUT_COMPOSITE_2, TV_INPUT_COMPONENT_1, TV_INPUT_COMPONENT_2, TV_INPUT_VGA_1, - TV_AUDIO_DESCRIPTION, TV_AUDIO_DESCRIPTION_MIX_UP, TV_AUDIO_DESCRIPTION_MIX_DOWN, - TV_ZOOM_MODE, TV_CONTENTS_MENU, TV_MEDIA_CONTEXT_MENU, TV_TIMER_PROGRAMMING, - Help, NAVIGATE_PREVIOUS, NAVIGATE_NEXT, NAVIGATE_IN, NAVIGATE_OUT, - STEM_PRIMARY, STEM_1, STEM_2, STEM_3, - Pointer_UpLeft, Pointer_DownLeft, Pointer_UpRight, Pointer_DownRight, - MEDIA_SKIP_FORWARD, MEDIA_SKIP_BACKWARD, MEDIA_STEP_FORWARD, MEDIA_STEP_BACKWARD, - SOFT_SLEEP, CUT, COPY, PASTE, - SYSTEM_NAVIGATION_UP, SYSTEM_NAVIGATION_DOWN, SYSTEM_NAVIGATION_LEFT, SYSTEM_NAVIGATION_RIGHT, - ALL_APPS, REFRESH, THUMBS_UP, THUMBS_DOWN, PROFILE_SWITCH, - A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, - exclam, quotedbl, dollar, percent, ampersand, colon, less, greater, question, asciicircum, underscore, braceleft, bar, braceright, asciitilde, + VoidSymbol, + SOFT_LEFT, + SOFT_RIGHT, + HOME, + BACK, + CALL, + ENDCALL, + _0, + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + asterisk, + numbersign, + Up, + Down, + Left, + Right, + KP_Begin, + VOLUME_UP, + VOLUME_DOWN, + POWER, + CAMERA, + Clear, + a, + b, + c, + d, + e, + f, + g, + h, + i, + j, + k, + l, + m, + n, + o, + p, + q, + r, + s, + t, + u, + v, + w, + x, + y, + z, + comma, + period, + Alt_L, + Alt_R, + Shift_L, + Shift_R, + Tab, + space, + SYM, + EXPLORER, + ENVELOPE, + Return, + BackSpace, + grave, + minus, + equal, + bracketleft, + bracketright, + backslash, + semicolon, + apostrophe, + slash, + at, + NUM, + HEADSETHOOK, + FOCUS, + plus, + Menu, + NOTIFICATION, + Find, + MEDIA_PLAY_PAUSE, + MEDIA_STOP, + MEDIA_NEXT, + MEDIA_PREVIOUS, + MEDIA_REWIND, + MEDIA_FAST_FORWARD, + MUTE, + Page_Up, + Page_Down, + PICTSYMBOLS, + Mode_switch, + BUTTON_A, + BUTTON_B, + BUTTON_C, + BUTTON_X, + BUTTON_Y, + BUTTON_Z, + BUTTON_L1, + BUTTON_R1, + BUTTON_L2, + BUTTON_R2, + BUTTON_THUMBL, + BUTTON_THUMBR, + BUTTON_START, + BUTTON_SELECT, + BUTTON_MODE, + Escape, + Delete, + Control_L, + Control_R, + Caps_Lock, + Scroll_Lock, + Meta_L, + Meta_R, + function, + Sys_Req, + Pause, + Home, + End, + Insert, + Next, + MEDIA_PLAY, + MEDIA_PAUSE, + MEDIA_CLOSE, + MEDIA_EJECT, + MEDIA_RECORD, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + Num_Lock, + KP_0, + KP_1, + KP_2, + KP_3, + KP_4, + KP_5, + KP_6, + KP_7, + KP_8, + KP_9, + KP_Divide, + KP_Multiply, + KP_Subtract, + KP_Add, + KP_Decimal, + KP_Separator, + KP_Enter, + KP_Equal, + parenleft, + parenright, + VOLUME_MUTE, + INFO, + CHANNEL_UP, + CHANNEL_DOWN, + ZOOM_IN, + ZOOM_OUT, + TV, + WINDOW, + GUIDE, + DVR, + BOOKMARK, + CAPTIONS, + SETTINGS, + TV_POWER, + TV_INPUT, + STB_POWER, + STB_INPUT, + AVR_POWER, + AVR_INPUT, + PROG_RED, + PROG_GREEN, + PROG_YELLOW, + PROG_BLUE, + APP_SWITCH, + BUTTON_1, + BUTTON_2, + BUTTON_3, + BUTTON_4, + BUTTON_5, + BUTTON_6, + BUTTON_7, + BUTTON_8, + BUTTON_9, + BUTTON_10, + BUTTON_11, + BUTTON_12, + BUTTON_13, + BUTTON_14, + BUTTON_15, + BUTTON_16, + LANGUAGE_SWITCH, + MANNER_MODE, + _3D_MODE, + CONTACTS, + CALENDAR, + MUSIC, + CALCULATOR, + Zenkaku_Hankaku, + Eisu_toggle, + Muhenkan, + Henkan, + Hiragana_Katakana, + yen, + RO, + Kana_Lock, + ASSIST, + BRIGHTNESS_DOWN, + BRIGHTNESS_UP, + MEDIA_AUDIO_TRACK, + SLEEP, + WAKEUP, + PAIRING, + MEDIA_TOP_MENU, + _11, + _12, + LAST_CHANNEL, + TV_DATA_SERVICE, + VOICE_ASSIST, + TV_RADIO_SERVICE, + TV_TELETEXT, + TV_NUMBER_ENTRY, + TV_TERRESTRIAL_ANALOG, + TV_TERRESTRIAL_DIGITAL, + TV_SATELLITE, + TV_SATELLITE_BS, + TV_SATELLITE_CS, + TV_SATELLITE_SERVICE, + TV_NETWORK, + TV_ANTENNA_CABLE, + TV_INPUT_HDMI_1, + TV_INPUT_HDMI_2, + TV_INPUT_HDMI_3, + TV_INPUT_HDMI_4, + TV_INPUT_COMPOSITE_1, + TV_INPUT_COMPOSITE_2, + TV_INPUT_COMPONENT_1, + TV_INPUT_COMPONENT_2, + TV_INPUT_VGA_1, + TV_AUDIO_DESCRIPTION, + TV_AUDIO_DESCRIPTION_MIX_UP, + TV_AUDIO_DESCRIPTION_MIX_DOWN, + TV_ZOOM_MODE, + TV_CONTENTS_MENU, + TV_MEDIA_CONTEXT_MENU, + TV_TIMER_PROGRAMMING, + Help, + NAVIGATE_PREVIOUS, + NAVIGATE_NEXT, + NAVIGATE_IN, + NAVIGATE_OUT, + STEM_PRIMARY, + STEM_1, + STEM_2, + STEM_3, + Pointer_UpLeft, + Pointer_DownLeft, + Pointer_UpRight, + Pointer_DownRight, + MEDIA_SKIP_FORWARD, + MEDIA_SKIP_BACKWARD, + MEDIA_STEP_FORWARD, + MEDIA_STEP_BACKWARD, + SOFT_SLEEP, + CUT, + COPY, + PASTE, + SYSTEM_NAVIGATION_UP, + SYSTEM_NAVIGATION_DOWN, + SYSTEM_NAVIGATION_LEFT, + SYSTEM_NAVIGATION_RIGHT, + ALL_APPS, + REFRESH, + THUMBS_UP, + THUMBS_DOWN, + PROFILE_SWITCH, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + exclam, + quotedbl, + dollar, + percent, + ampersand, + colon, + less, + greater, + question, + asciicircum, + underscore, + braceleft, + bar, + braceright, + asciitilde, ; companion object { - // librime keyname (x11) - trime keycode (兼容Android) private val convertMap: HashMap = hashMapOf() @@ -148,7 +431,10 @@ enum class Keycode { return keycode in SOFT_LEFT.ordinal..PROFILE_SWITCH.ordinal } - fun toStdKeyEvent(keycode: Int, mask: Int = 0): IntArray { + fun toStdKeyEvent( + keycode: Int, + mask: Int = 0, + ): IntArray { val event = IntArray(2) if (keycode !in values().indices) return event if (keycode < A.ordinal) { @@ -158,24 +444,25 @@ enum class Keycode { if (keycode <= Z.ordinal) { event[0] = keycode - A.ordinal + a.ordinal } else { - event[0] = when (keycode) { - exclam.ordinal -> _1.ordinal - dollar.ordinal -> _4.ordinal - percent.ordinal -> _5.ordinal - asciicircum.ordinal -> _6.ordinal - ampersand.ordinal -> _7.ordinal - quotedbl.ordinal -> apostrophe.ordinal - colon.ordinal -> semicolon.ordinal - less.ordinal -> comma.ordinal - greater.ordinal -> period.ordinal - question.ordinal -> slash.ordinal - underscore.ordinal -> minus.ordinal - braceleft.ordinal -> bracketleft.ordinal - braceright.ordinal -> bracketright.ordinal - asciitilde.ordinal -> grave.ordinal - bar.ordinal -> backslash.ordinal - else -> 0 - } + event[0] = + when (keycode) { + exclam.ordinal -> _1.ordinal + dollar.ordinal -> _4.ordinal + percent.ordinal -> _5.ordinal + asciicircum.ordinal -> _6.ordinal + ampersand.ordinal -> _7.ordinal + quotedbl.ordinal -> apostrophe.ordinal + colon.ordinal -> semicolon.ordinal + less.ordinal -> comma.ordinal + greater.ordinal -> period.ordinal + question.ordinal -> slash.ordinal + underscore.ordinal -> minus.ordinal + braceleft.ordinal -> bracketleft.ordinal + braceright.ordinal -> bracketright.ordinal + asciitilde.ordinal -> grave.ordinal + bar.ordinal -> backslash.ordinal + else -> 0 + } } event[1] = mask or KeyEvent.META_SHIFT_ON } @@ -191,7 +478,10 @@ enum class Keycode { return reverseMap[keycode] ?: "" } - fun getDisplayLabel(keyCode: Int, mask: Int): String = + fun getDisplayLabel( + keyCode: Int, + mask: Int, + ): String = if (isStdKey(keyCode)) { // Android keycode区域 if (Key.getKcm().isPrintingKey(keyCode)) { @@ -212,13 +502,14 @@ enum class Keycode { "" } - private val masks = hashMapOf( - "Shift" to KeyEvent.META_SHIFT_ON, - "Control" to KeyEvent.META_CTRL_ON, - "Alt" to KeyEvent.META_ALT_ON, - "Meta" to KeyEvent.META_META_ON, - "SYM" to KeyEvent.META_SYM_ON, - ) + private val masks = + hashMapOf( + "Shift" to KeyEvent.META_SHIFT_ON, + "Control" to KeyEvent.META_CTRL_ON, + "Alt" to KeyEvent.META_ALT_ON, + "Meta" to KeyEvent.META_META_ON, + "SYM" to KeyEvent.META_SYM_ON, + ) @JvmStatic fun fromString(s: String): Keycode { diff --git a/app/src/main/java/com/osfans/trime/ime/enums/PopupPosition.kt b/app/src/main/java/com/osfans/trime/ime/enums/PopupPosition.kt index 66ade4083a..dcd4415125 100644 --- a/app/src/main/java/com/osfans/trime/ime/enums/PopupPosition.kt +++ b/app/src/main/java/com/osfans/trime/ime/enums/PopupPosition.kt @@ -22,16 +22,26 @@ package com.osfans.trime.ime.enums */ enum class PopupPosition { // 跟随光标 - LEFT, LEFT_UP, RIGHT, RIGHT_UP, + LEFT, + LEFT_UP, + RIGHT, + RIGHT_UP, // 固定位置 - DRAG, FIXED, + DRAG, + FIXED, // 相对位置 - BOTTOM_LEFT, BOTTOM_RIGHT, TOP_LEFT, TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT, + TOP_LEFT, + TOP_RIGHT, // 暂未实现的相对位置 - TOP_CENTER, BOTTOM_CENTER, CENTER; + TOP_CENTER, + BOTTOM_CENTER, + CENTER, + ; companion object { @JvmStatic @@ -42,7 +52,10 @@ enum class PopupPosition { } @JvmStatic - fun fromString(code: String, default: PopupPosition): PopupPosition { + fun fromString( + code: String, + default: PopupPosition, + ): PopupPosition { return runCatching { valueOf(code.uppercase()) }.getOrDefault(default) diff --git a/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt b/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt index 943572a694..ea58427290 100644 --- a/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt +++ b/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt @@ -42,13 +42,14 @@ class InputFeedbackManager( fun resumeSoundPool() { SoundThemeManager.getActiveSoundFilePaths().onSuccess { path -> - soundPool = SoundPool.Builder() - .setMaxStreams(1) - .setAudioAttributes( - AudioAttributes.Builder() - .setLegacyStreamType(AudioManager.STREAM_SYSTEM) - .build(), - ).build() + soundPool = + SoundPool.Builder() + .setMaxStreams(1) + .setAudioAttributes( + AudioAttributes.Builder() + .setLegacyStreamType(AudioManager.STREAM_SYSTEM) + .build(), + ).build() soundIds.clear() soundIds.addAll(path.map { soundPool!!.load(it, 1) }) } @@ -69,19 +70,20 @@ class InputFeedbackManager( val vibrationDuration = prefs.keyboard.vibrationDuration.toLong() var vibrationAmplitude = prefs.keyboard.vibrationAmplitude - val hapticsPerformed = if (vibrationDuration < 0) { - ims.window?.window?.decorView?.performHapticFeedback( - HapticFeedbackConstants.KEYBOARD_TAP, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING - } else { - @Suppress("DEPRECATION") - HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING or HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING - }, - ) - } else { - false - } + val hapticsPerformed = + if (vibrationDuration < 0) { + ims.window?.window?.decorView?.performHapticFeedback( + HapticFeedbackConstants.KEYBOARD_TAP, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING + } else { + @Suppress("DEPRECATION") + HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING or HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING + }, + ) + } else { + false + } if (hapticsPerformed == true) { return @@ -110,13 +112,18 @@ class InputFeedbackManager( get() { return tts?.voice?.locale } - set(v) { tts?.language = v } + set(v) { + tts?.language = v + } fun resetPlayProgress() { if (playProgress > 0) playProgress = 0 } - private fun playCustomSoundEffect(keycode: Int, volume: Float) { + private fun playCustomSoundEffect( + keycode: Int, + volume: Float, + ) { SoundThemeManager.getActiveSoundTheme().onSuccess { theme -> if (theme.sound.isEmpty()) return val sounds = theme.sound @@ -145,12 +152,13 @@ class InputFeedbackManager( if (prefs.keyboard.customSoundEnabled) { playCustomSoundEffect(keyCode, soundVolume) } else { - val effect = when (keyCode) { - KeyEvent.KEYCODE_SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR - KeyEvent.KEYCODE_DEL -> AudioManager.FX_KEYPRESS_DELETE - KeyEvent.KEYCODE_ENTER -> AudioManager.FX_KEYPRESS_RETURN - else -> AudioManager.FX_KEYPRESS_STANDARD - } + val effect = + when (keyCode) { + KeyEvent.KEYCODE_SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR + KeyEvent.KEYCODE_DEL -> AudioManager.FX_KEYPRESS_DELETE + KeyEvent.KEYCODE_ENTER -> AudioManager.FX_KEYPRESS_RETURN + else -> AudioManager.FX_KEYPRESS_STANDARD + } audioManager.playSoundEffect( effect, soundVolume, @@ -174,16 +182,17 @@ class InputFeedbackManager( } private inline fun contentSpeakInternal(content: T) { - val text = when { - 0 is T -> { - KeyEvent.keyCodeToString(content as Int) - .replace("KEYCODE_", "") - .replace("_", " ") - .lowercase(Locale.getDefault()) - } - "" is T -> content as String - else -> null - } ?: return + val text = + when { + 0 is T -> { + KeyEvent.keyCodeToString(content as Int) + .replace("KEYCODE_", "") + .replace("_", " ") + .lowercase(Locale.getDefault()) + } + "" is T -> content as String + else -> null + } ?: return tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null, "TrimeTTS") } 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 f1c2745eef..1686eea068 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 @@ -39,24 +39,29 @@ object KeyboardSwitcher { } fun switchKeyboard(name: String?) { - val i = when (name) { - ".default" -> 0 - ".prior" -> currentId - 1 - ".next" -> currentId + 1 - ".last" -> lastId - ".last_lock" -> lastLockId - ".ascii" -> { - val asciiKeyboard = availableKeyboards[currentId].asciiKeyboard - if (asciiKeyboard.isNullOrEmpty()) { currentId } else { availableKeyboardIds.indexOf(asciiKeyboard) } - } - else -> { - if (name.isNullOrEmpty()) { - if (availableKeyboards[currentId].isLock) currentId else lastLockId - } else { - availableKeyboardIds.indexOf(name) + val i = + when (name) { + ".default" -> 0 + ".prior" -> currentId - 1 + ".next" -> currentId + 1 + ".last" -> lastId + ".last_lock" -> lastLockId + ".ascii" -> { + val asciiKeyboard = availableKeyboards[currentId].asciiKeyboard + if (asciiKeyboard.isNullOrEmpty()) { + currentId + } else { + availableKeyboardIds.indexOf(asciiKeyboard) + } + } + else -> { + if (name.isNullOrEmpty()) { + if (availableKeyboards[currentId].isLock) currentId else lastLockId + } else { + availableKeyboardIds.indexOf(name) + } } } - } val deviceKeyboard = appContext.resources.configuration.keyboard var mini = -1 diff --git a/app/src/main/java/com/osfans/trime/ime/landscapeinput/LandscapeInputUIMode.kt b/app/src/main/java/com/osfans/trime/ime/landscapeinput/LandscapeInputUIMode.kt index 60385a724d..83d07d2baf 100644 --- a/app/src/main/java/com/osfans/trime/ime/landscapeinput/LandscapeInputUIMode.kt +++ b/app/src/main/java/com/osfans/trime/ime/landscapeinput/LandscapeInputUIMode.kt @@ -11,6 +11,7 @@ enum class LandscapeInputUIMode { companion object { private val convertMap: HashMap = hashMapOf() + fun fromString(mode: String): LandscapeInputUIMode { val type = convertMap[mode.uppercase(Locale.getDefault())] return type ?: AUTO_SHOW diff --git a/app/src/main/java/com/osfans/trime/ime/symbol/FlexibleAdapter.kt b/app/src/main/java/com/osfans/trime/ime/symbol/FlexibleAdapter.kt index 2fb4e65cd5..7329991233 100644 --- a/app/src/main/java/com/osfans/trime/ime/symbol/FlexibleAdapter.kt +++ b/app/src/main/java/com/osfans/trime/ime/symbol/FlexibleAdapter.kt @@ -35,16 +35,17 @@ class FlexibleAdapter( @SuppressLint("NotifyDataSetChanged") fun updateBeans(beans: List) { - val sorted = beans.sortedWith { b1, b2 -> - when { - // 如果 b1 置顶而 b2 没置顶,则 b1 比 b2 小,排前面 - b1.pinned && !b2.pinned -> -1 - // 如果 b1 没置顶而 b2 置顶,则 b1 比 b2 大,排后面 - !b1.pinned && b2.pinned -> 1 - // 如果都置顶了或都没置顶,则比较 id,id 大的排前面 - else -> b2.id.compareTo(b1.id) + val sorted = + beans.sortedWith { b1, b2 -> + when { + // 如果 b1 置顶而 b2 没置顶,则 b1 比 b2 小,排前面 + b1.pinned && !b2.pinned -> -1 + // 如果 b1 没置顶而 b2 置顶,则 b1 比 b2 大,排后面 + !b1.pinned && b2.pinned -> 1 + // 如果都置顶了或都没置顶,则比较 id,id 大的排前面 + else -> b2.id.compareTo(b1.id) + } } - } mBeans.clear() mBeans.addAll(sorted) mBeansId.clear() @@ -57,7 +58,10 @@ class FlexibleAdapter( override fun getItemCount(): Int = mBeans.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ViewHolder { val binding = SimpleKeyItemBinding.inflate(LayoutInflater.from(parent.context)) return ViewHolder(binding) } @@ -67,7 +71,10 @@ class FlexibleAdapter( val simpleKeyPin = binding.simpleKeyPin } - override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + override fun onBindViewHolder( + viewHolder: ViewHolder, + position: Int, + ) { with(viewHolder) { val bean = mBeans[position] simpleKeyText.apply { @@ -80,22 +87,24 @@ class FlexibleAdapter( val longTextSize = theme.style.getFloat("key_long_text_size") val labelTextSize = theme.style.getFloat("label_text_size") - textSize = when { - longTextSize > 0 -> longTextSize - labelTextSize > 0 -> labelTextSize - else -> textSize - } + textSize = + when { + longTextSize > 0 -> longTextSize + labelTextSize > 0 -> labelTextSize + else -> textSize + } } simpleKeyPin.visibility = if (bean.pinned) View.VISIBLE else View.INVISIBLE // if (background != null) viewHolder.itemView.setBackground(background); - (itemView as CardView).background = theme.colors.getDrawable( - "long_text_back_color", - "key_border", - "key_long_text_border", - "round_corner", - null, - ) + (itemView as CardView).background = + theme.colors.getDrawable( + "long_text_back_color", + "key_border", + "key_long_text_border", + "round_corner", + null, + ) // 如果设置了回调,则设置点击事件 if (this@FlexibleAdapter::listener.isInitialized) { @@ -189,7 +198,10 @@ class FlexibleAdapter( notifyItemRemoved(position) } - private fun setPinStatus(id: Int, pinned: Boolean) { + private fun setPinStatus( + id: Int, + pinned: Boolean, + ) { val position = mBeansId.getValue(id) mBeans[position] = mBeans[position].copy(pinned = pinned) // 置顶会改变条目的排列顺序 @@ -198,24 +210,28 @@ class FlexibleAdapter( private fun askToDeleteAll() { val service = Trime.getService() - val askDialog = AlertDialog.Builder( - appContext, - androidx.appcompat.R.style.Theme_AppCompat_DayNight_Dialog_Alert, - ).setTitle(R.string.liquid_keyboard_ask_to_delete_all) - .setPositiveButton(R.string.ok) { dialog, which -> - service.lifecycleScope.launch { - listener.onDeleteAll() - } - }.setNegativeButton(R.string.cancel) { dialog, which -> - }.create() + val askDialog = + AlertDialog.Builder( + appContext, + androidx.appcompat.R.style.Theme_AppCompat_DayNight_Dialog_Alert, + ).setTitle(R.string.liquid_keyboard_ask_to_delete_all) + .setPositiveButton(R.string.ok) { dialog, which -> + service.lifecycleScope.launch { + listener.onDeleteAll() + } + }.setNegativeButton(R.string.cancel) { dialog, which -> + }.create() service.showDialogAboveInputView(askDialog) } // 添加回调 interface Listener { fun onPaste(bean: DatabaseBean) + suspend fun onPin(bean: DatabaseBean) + suspend fun onUnpin(bean: DatabaseBean) + suspend fun onDelete(bean: DatabaseBean) suspend fun onEdit(bean: DatabaseBean) diff --git a/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.kt b/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.kt index 86004c7483..da52882538 100644 --- a/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.kt +++ b/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.kt @@ -44,6 +44,7 @@ class LiquidKeyboard(private val context: Context) : ClipboardHelper.OnClipboard } // 及时更新layoutManager, 以防在旋转屏幕后打开液体键盘crash + /** * 使用FlexboxLayoutManager时调用此函数获取 */ @@ -96,51 +97,45 @@ class LiquidKeyboard(private val context: Context) : ClipboardHelper.OnClipboard private fun initFixData(i: Int) { val tabTag = TabManager.getTag(i) - val simpleAdapter = SimpleAdapter(theme).apply { - // 列表适配器的点击监听事件 - setListener { position -> - val bean = beans[position] - if (tabTag.type === SymbolKeyboardType.SYMBOL) { - service.inputSymbol(bean.text) - } else if (tabTag.type !== SymbolKeyboardType.TABS) { - service.currentInputConnection?.run { - commitText(bean.text, 1) - if (tabTag.type !== SymbolKeyboardType.HISTORY) { - symbolHistory.insert(bean.text) - symbolHistory.save() - } - } - } else { - val tag = TabManager.get().getTabSwitchTabTag(position) - val truePosition = TabManager.get().getTabSwitchPosition(position) - Timber.v( - "TABS click: " + - "position = $position, truePosition = $truePosition, tag.text = ${tag.text}", - ) - if (tag.type === SymbolKeyboardType.NO_KEY) { - when (tag.command) { - KeyCommandType.EXIT -> service.selectLiquidKeyboard(-1) - KeyCommandType.DEL_LEFT, KeyCommandType.DEL_RIGHT, KeyCommandType.REDO, KeyCommandType.UNDO -> {} - else -> {} + val simpleAdapter = + SimpleAdapter(theme).apply { + // 列表适配器的点击监听事件 + setListener { position -> + val bean = beans[position] + if (tabTag.type === SymbolKeyboardType.SYMBOL) { + service.inputSymbol(bean.text) + } else if (tabTag.type !== SymbolKeyboardType.TABS) { + service.currentInputConnection?.run { + commitText(bean.text, 1) + if (tabTag.type !== SymbolKeyboardType.HISTORY) { + symbolHistory.insert(bean.text) + symbolHistory.save() + } } - } else if (TabManager.get().isAfterTabSwitch(truePosition)) { - // tab的位置在“更多”的右侧,不滚动tab,焦点仍然在”更多“上 - select(truePosition) } else { - service.selectLiquidKeyboard(truePosition) + val tag = TabManager.get().getTabSwitchTabTag(position) + val truePosition = TabManager.get().getTabSwitchPosition(position) + Timber.v( + "TABS click: " + + "position = $position, truePosition = $truePosition, tag.text = ${tag.text}", + ) + if (tag.type === SymbolKeyboardType.NO_KEY) { + when (tag.command) { + KeyCommandType.EXIT -> service.selectLiquidKeyboard(-1) + KeyCommandType.DEL_LEFT, KeyCommandType.DEL_RIGHT, KeyCommandType.REDO, KeyCommandType.UNDO -> {} + else -> {} + } + } else if (TabManager.get().isAfterTabSwitch(truePosition)) { + // tab的位置在“更多”的右侧,不滚动tab,焦点仍然在”更多“上 + select(truePosition) + } else { + service.selectLiquidKeyboard(truePosition) + } } } } - } keyboardView.apply { layoutManager = getFlexbox() - /* - Timber.d( - "configStylet() single_width=%s, keyHeight=%s, margin_x=%s, margin_top=%s", - singleWidth, keyHeight, marginLeft, marginTop - ) **/ - // simpleAdapter!!.configStyle(singleWidth, keyHeight, marginLeft, marginTop) - // simpleAdapter.configKey(single_width,height,margin_x,margin_top); adapter = simpleAdapter // 添加分割线 // 设置添加删除动画 @@ -162,78 +157,81 @@ class LiquidKeyboard(private val context: Context) : ClipboardHelper.OnClipboard } private fun initDbData(type: SymbolKeyboardType) { - val dbAdapter = FlexibleAdapter(theme).apply { - setListener(object : FlexibleAdapter.Listener { - override fun onPaste(bean: DatabaseBean) { - service.currentInputConnection?.commitText(bean.text, 1) - } - - override suspend fun onPin(bean: DatabaseBean) { - when (type) { - SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.pin(bean.id) - SymbolKeyboardType.COLLECTION -> CollectionHelper.pin(bean.id) - SymbolKeyboardType.DRAFT -> DraftHelper.pin(bean.id) - else -> return - } - } + val dbAdapter = + FlexibleAdapter(theme).apply { + setListener( + object : FlexibleAdapter.Listener { + override fun onPaste(bean: DatabaseBean) { + service.currentInputConnection?.commitText(bean.text, 1) + } - override suspend fun onUnpin(bean: DatabaseBean) { - when (type) { - SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.unpin(bean.id) - SymbolKeyboardType.COLLECTION -> CollectionHelper.unpin(bean.id) - SymbolKeyboardType.DRAFT -> DraftHelper.unpin(bean.id) - else -> return - } - } + override suspend fun onPin(bean: DatabaseBean) { + when (type) { + SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.pin(bean.id) + SymbolKeyboardType.COLLECTION -> CollectionHelper.pin(bean.id) + SymbolKeyboardType.DRAFT -> DraftHelper.pin(bean.id) + else -> return + } + } - override suspend fun onDelete(bean: DatabaseBean) { - when (type) { - SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.delete(bean.id) - SymbolKeyboardType.COLLECTION -> CollectionHelper.delete(bean.id) - SymbolKeyboardType.DRAFT -> DraftHelper.delete(bean.id) - else -> return - } - } + override suspend fun onUnpin(bean: DatabaseBean) { + when (type) { + SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.unpin(bean.id) + SymbolKeyboardType.COLLECTION -> CollectionHelper.unpin(bean.id) + SymbolKeyboardType.DRAFT -> DraftHelper.unpin(bean.id) + else -> return + } + } - override suspend fun onEdit(bean: DatabaseBean) { - bean.text?.let { launchLiquidKeyboardEditText(context, type, bean.id, it) } - } + override suspend fun onDelete(bean: DatabaseBean) { + when (type) { + SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.delete(bean.id) + SymbolKeyboardType.COLLECTION -> CollectionHelper.delete(bean.id) + SymbolKeyboardType.DRAFT -> DraftHelper.delete(bean.id) + else -> return + } + } - // FIXME: 这个方法可能实现得比较粗糙,需要日后改进 - @SuppressLint("NotifyDataSetChanged") - override suspend fun onDeleteAll() { - if (beans.all { it.pinned }) { - // 如果没有未置顶的条目,则删除所有已置顶的条目 - when (type) { - SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.deleteAll(false) - SymbolKeyboardType.COLLECTION -> CollectionHelper.deleteAll(false) - SymbolKeyboardType.DRAFT -> DraftHelper.deleteAll(false) - else -> return + override suspend fun onEdit(bean: DatabaseBean) { + bean.text?.let { launchLiquidKeyboardEditText(context, type, bean.id, it) } } - updateBeans(emptyList()) - } else { - // 如果有已置顶的条目,则删除所有未置顶的条目 - when (type) { - SymbolKeyboardType.CLIPBOARD -> { - ClipboardHelper.deleteAll() - updateBeans(ClipboardHelper.getAll()) - } - SymbolKeyboardType.COLLECTION -> { - CollectionHelper.deleteAll() - updateBeans(CollectionHelper.getAll()) - } - SymbolKeyboardType.DRAFT -> { - DraftHelper.deleteAll() - updateBeans(DraftHelper.getAll()) + + // FIXME: 这个方法可能实现得比较粗糙,需要日后改进 + @SuppressLint("NotifyDataSetChanged") + override suspend fun onDeleteAll() { + if (beans.all { it.pinned }) { + // 如果没有未置顶的条目,则删除所有已置顶的条目 + when (type) { + SymbolKeyboardType.CLIPBOARD -> ClipboardHelper.deleteAll(false) + SymbolKeyboardType.COLLECTION -> CollectionHelper.deleteAll(false) + SymbolKeyboardType.DRAFT -> DraftHelper.deleteAll(false) + else -> return + } + updateBeans(emptyList()) + } else { + // 如果有已置顶的条目,则删除所有未置顶的条目 + when (type) { + SymbolKeyboardType.CLIPBOARD -> { + ClipboardHelper.deleteAll() + updateBeans(ClipboardHelper.getAll()) + } + SymbolKeyboardType.COLLECTION -> { + CollectionHelper.deleteAll() + updateBeans(CollectionHelper.getAll()) + } + SymbolKeyboardType.DRAFT -> { + DraftHelper.deleteAll() + updateBeans(DraftHelper.getAll()) + } + else -> return + } } - else -> return } - } - } - override val showCollectButton: Boolean = type != SymbolKeyboardType.COLLECTION - }) - } + override val showCollectButton: Boolean = type != SymbolKeyboardType.COLLECTION + }, + ) + } keyboardView.apply { layoutManager = getOneColumnStaggeredGrid() adapter = dbAdapter @@ -264,18 +262,19 @@ class LiquidKeyboard(private val context: Context) : ClipboardHelper.OnClipboard } private fun initCandidates() { - val candidateAdapter = CandidateAdapter(theme).apply { - setListener { position -> - TextInputManager.getInstance().onCandidatePressed(position) - if (Rime.isComposing) { - updateCandidates(Rime.candidatesWithoutSwitch.toList()) - notifyDataSetChanged() - keyboardView.scrollToPosition(0) - } else { - service.selectLiquidKeyboard(-1) + val candidateAdapter = + CandidateAdapter(theme).apply { + setListener { position -> + TextInputManager.getInstance().onCandidatePressed(position) + if (Rime.isComposing) { + updateCandidates(Rime.candidatesWithoutSwitch.toList()) + notifyDataSetChanged() + keyboardView.scrollToPosition(0) + } else { + service.selectLiquidKeyboard(-1) + } } } - } // 设置布局管理器 keyboardView.apply { layoutManager = getFlexbox() @@ -288,11 +287,12 @@ class LiquidKeyboard(private val context: Context) : ClipboardHelper.OnClipboard } private fun initVarLengthKeys(data: List) { - val candidateAdapter = CandidateAdapter(theme).apply { - setListener { position -> - service.currentInputConnection?.commitText(data[position].text, 1) + val candidateAdapter = + CandidateAdapter(theme).apply { + setListener { position -> + service.currentInputConnection?.commitText(data[position].text, 1) + } } - } // 设置布局管理器 keyboardView.apply { layoutManager = getFlexbox() diff --git a/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt b/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt index 8ca7d79880..e22c801d23 100644 --- a/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt +++ b/app/src/main/java/com/osfans/trime/ime/text/TextInputManager.kt @@ -57,529 +57,550 @@ class TextInputManager private constructor() : Trime.EventListener, KeyboardView.OnKeyboardActionListener, Candidate.EventListener { - private val trime get() = Trime.getService() - private val prefs get() = AppPrefs.defaultInstance() - private val activeEditorInstance: EditorInstance - get() = trime.activeEditorInstance - private var intentReceiver: IntentReceiver? = null - private var rimeNotiHandlerJob: Job? = null - - private var mainKeyboardView: KeyboardView? = null - var candidateRoot: ScrollView? = null - var candidateView: Candidate? = null - - val locales = Array(2) { Locale.getDefault() } - - var needSendUpRimeKey: Boolean = false - var shouldUpdateRimeOption: Boolean = true - var isComposable: Boolean = false - var shouldResetAsciiMode: Boolean = false - - companion object { - /** Delimiter regex for key property group, their format like `{property_1: value_1, property_2: value_2}` */ - private val DELIMITER_PROPERTY_GROUP = """^(\{[^{}]+\}).*$""".toRegex() - - /** Delimiter regex for property key tag, its format like `Escape: ` following a property group like above */ - private val DELIMITER_PROPERTY_KEY = """^((\{Escape\})?[^{}]+).*$""".toRegex() - - /** Delimiter regex to split language/locale tags. */ - private val DELIMITER_SPLITTER = """[-_]""".toRegex() - private var instance: TextInputManager? = null - - fun getInstance(): TextInputManager { - if (instance == null) { - instance = TextInputManager() + private val trime get() = Trime.getService() + private val prefs get() = AppPrefs.defaultInstance() + private val activeEditorInstance: EditorInstance + get() = trime.activeEditorInstance + private var intentReceiver: IntentReceiver? = null + private var rimeNotiHandlerJob: Job? = null + + private var mainKeyboardView: KeyboardView? = null + var candidateRoot: ScrollView? = null + var candidateView: Candidate? = null + + val locales = Array(2) { Locale.getDefault() } + + var needSendUpRimeKey: Boolean = false + var shouldUpdateRimeOption: Boolean = true + var isComposable: Boolean = false + var shouldResetAsciiMode: Boolean = false + + companion object { + /** Delimiter regex for key property group, their format like `{property_1: value_1, property_2: value_2}` */ + private val DELIMITER_PROPERTY_GROUP = """^(\{[^{}]+\}).*$""".toRegex() + + /** Delimiter regex for property key tag, its format like `Escape: ` following a property group like above */ + private val DELIMITER_PROPERTY_KEY = """^((\{Escape\})?[^{}]+).*$""".toRegex() + + /** Delimiter regex to split language/locale tags. */ + private val DELIMITER_SPLITTER = """[-_]""".toRegex() + private var instance: TextInputManager? = null + + fun getInstance(): TextInputManager { + if (instance == null) { + instance = TextInputManager() + } + return instance!! } - return instance!! } - } - init { - trime.addEventListener(this) - } + init { + trime.addEventListener(this) + } - /** - * Non-UI-related setup + preloading of all required computed layouts (asynchronous in the - * background). - */ - override fun onCreate() { - super.onCreate() + /** + * Non-UI-related setup + preloading of all required computed layouts (asynchronous in the + * background). + */ + override fun onCreate() { + super.onCreate() - intentReceiver = IntentReceiver().also { - it.registerReceiver(trime) - } - rimeNotiHandlerJob = Rime.getInstance().notificationFlow - .onEach(::handleRimeNotification) - .launchIn(trime.lifecycleScope) - - val theme = Theme.get() - val defaultLocale = theme.style.getString("locale").split(DELIMITER_SPLITTER) - locales[0] = when (defaultLocale.size) { - 3 -> Locale(defaultLocale[0], defaultLocale[1], defaultLocale[2]) - 2 -> Locale(defaultLocale[0], defaultLocale[1]) - else -> Locale.getDefault() - } + intentReceiver = + IntentReceiver().also { + it.registerReceiver(trime) + } + rimeNotiHandlerJob = + Rime.getInstance().notificationFlow + .onEach(::handleRimeNotification) + .launchIn(trime.lifecycleScope) + + val theme = Theme.get() + val defaultLocale = theme.style.getString("locale").split(DELIMITER_SPLITTER) + locales[0] = + when (defaultLocale.size) { + 3 -> Locale(defaultLocale[0], defaultLocale[1], defaultLocale[2]) + 2 -> Locale(defaultLocale[0], defaultLocale[1]) + else -> Locale.getDefault() + } - val latinLocale = theme.style.getString("latin_locale").split(DELIMITER_SPLITTER) - locales[1] = when (latinLocale.size) { - 3 -> Locale(latinLocale[0], latinLocale[1], latinLocale[2]) - 2 -> Locale(latinLocale[0], latinLocale[1]) - else -> Locale.US + val latinLocale = theme.style.getString("latin_locale").split(DELIMITER_SPLITTER) + locales[1] = + when (latinLocale.size) { + 3 -> Locale(latinLocale[0], latinLocale[1], latinLocale[2]) + 2 -> Locale(latinLocale[0], latinLocale[1]) + else -> Locale.US + } + // preload all required parameters + trime.loadConfig() } - // preload all required parameters - trime.loadConfig() - } - override fun onInitializeInputUi(uiBinding: InputRootBinding) { - super.onInitializeInputUi(uiBinding) - // Initialize main keyboard view - mainKeyboardView = uiBinding.main.mainKeyboardView.also { - it.setOnKeyboardActionListener(this) - it.setShowHint(!Rime.getOption("_hide_key_hint")) - it.setShowSymbol(!Rime.getOption("_hide_key_symbol")) - it.reset() - } - // Initialize candidate bar - candidateRoot = uiBinding.main.candidateView.candidateRoot.also { - it.setPageStr( - Runnable { trime.handleKey(KeyEvent.KEYCODE_PAGE_DOWN, 0) }, - Runnable { trime.handleKey(KeyEvent.KEYCODE_PAGE_UP, 0) }, - Runnable { trime.selectLiquidKeyboard(SymbolKeyboardType.CANDIDATE) }, - ) - it.visibility = if (Rime.getOption("_hide_candidate")) View.GONE else View.VISIBLE - } + override fun onInitializeInputUi(uiBinding: InputRootBinding) { + super.onInitializeInputUi(uiBinding) + // Initialize main keyboard view + mainKeyboardView = + uiBinding.main.mainKeyboardView.also { + it.setOnKeyboardActionListener(this) + it.setShowHint(!Rime.getOption("_hide_key_hint")) + it.setShowSymbol(!Rime.getOption("_hide_key_symbol")) + it.reset() + } + // Initialize candidate bar + candidateRoot = + uiBinding.main.candidateView.candidateRoot.also { + it.setPageStr( + Runnable { trime.handleKey(KeyEvent.KEYCODE_PAGE_DOWN, 0) }, + Runnable { trime.handleKey(KeyEvent.KEYCODE_PAGE_UP, 0) }, + Runnable { trime.selectLiquidKeyboard(SymbolKeyboardType.CANDIDATE) }, + ) + it.visibility = if (Rime.getOption("_hide_candidate")) View.GONE else View.VISIBLE + } - candidateView = uiBinding.main.candidateView.candidates.also { - it.setCandidateListener(this) - it.setShowComment(!Rime.getOption("_hide_comment")) - it.reset() + candidateView = + uiBinding.main.candidateView.candidates.also { + it.setCandidateListener(this) + it.setShowComment(!Rime.getOption("_hide_comment")) + it.reset() + } } - } - /** - * Cancels all coroutines and cleans up. - */ - override fun onDestroy() { - intentReceiver?.unregisterReceiver(trime) - intentReceiver = null + /** + * Cancels all coroutines and cleans up. + */ + override fun onDestroy() { + intentReceiver?.unregisterReceiver(trime) + intentReceiver = null - candidateView?.setCandidateListener(null) - candidateView = null + candidateView?.setCandidateListener(null) + candidateView = null - candidateRoot = null + candidateRoot = null - mainKeyboardView?.setOnKeyboardActionListener(null) - mainKeyboardView = null + mainKeyboardView?.setOnKeyboardActionListener(null) + mainKeyboardView = null - rimeNotiHandlerJob?.cancel() - rimeNotiHandlerJob = null - instance = null - } + rimeNotiHandlerJob?.cancel() + rimeNotiHandlerJob = null + instance = null + } - override fun onStartInputView(instance: EditorInstance, restarting: Boolean) { - super.onStartInputView(instance, restarting) - Trime.getService().selectLiquidKeyboard(-1) - isComposable = false - var tempAsciiMode = if (shouldResetAsciiMode) false else null - val keyboardType = - when (instance.editorInfo!!.imeOptions and EditorInfo.IME_FLAG_FORCE_ASCII) { - EditorInfo.IME_FLAG_FORCE_ASCII -> { - tempAsciiMode = true - ".ascii" - } - else -> { - val inputAttrsRaw = instance.editorInfo!!.inputType - isComposable = inputAttrsRaw > 0 - when (inputAttrsRaw and InputType.TYPE_MASK_CLASS) { - InputType.TYPE_CLASS_NUMBER, - InputType.TYPE_CLASS_PHONE, - InputType.TYPE_CLASS_DATETIME, - -> { - "number" - } - InputType.TYPE_CLASS_TEXT -> { - when (inputAttrsRaw and InputType.TYPE_MASK_VARIATION) { - InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE -> { - null - } - InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, - InputType.TYPE_TEXT_VARIATION_PASSWORD, - InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, - InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, - InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, - -> { - Timber.i( - "EditorInfo: " + - " inputAttrsRaw" + inputAttrsRaw + - "; InputType" + (inputAttrsRaw and InputType.TYPE_MASK_VARIATION), - ) - - tempAsciiMode = true - ".ascii" + override fun onStartInputView( + instance: EditorInstance, + restarting: Boolean, + ) { + super.onStartInputView(instance, restarting) + Trime.getService().selectLiquidKeyboard(-1) + isComposable = false + var tempAsciiMode = if (shouldResetAsciiMode) false else null + val keyboardType = + when (instance.editorInfo!!.imeOptions and EditorInfo.IME_FLAG_FORCE_ASCII) { + EditorInfo.IME_FLAG_FORCE_ASCII -> { + tempAsciiMode = true + ".ascii" + } + else -> { + val inputAttrsRaw = instance.editorInfo!!.inputType + isComposable = inputAttrsRaw > 0 + when (inputAttrsRaw and InputType.TYPE_MASK_CLASS) { + InputType.TYPE_CLASS_NUMBER, + InputType.TYPE_CLASS_PHONE, + InputType.TYPE_CLASS_DATETIME, + -> { + "number" + } + InputType.TYPE_CLASS_TEXT -> { + when (inputAttrsRaw and InputType.TYPE_MASK_VARIATION) { + InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE -> { + null + } + InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, + InputType.TYPE_TEXT_VARIATION_PASSWORD, + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, + InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, + InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, + -> { + Timber.i( + "EditorInfo: " + + " inputAttrsRaw" + inputAttrsRaw + + "; InputType" + (inputAttrsRaw and InputType.TYPE_MASK_VARIATION), + ) + + tempAsciiMode = true + ".ascii" + } + else -> null.also { isComposable = true } } - else -> null.also { isComposable = true } } - } - else -> { - if (inputAttrsRaw <= 0) return - null + else -> { + if (inputAttrsRaw <= 0) return + null + } } } } - } - KeyboardSwitcher.run { - resize(trime.maxWidth) - // Select a keyboard based on the input type of the editing field. - switchKeyboard(keyboardType) - } - Rime.getInstance() + KeyboardSwitcher.run { + resize(trime.maxWidth) + // Select a keyboard based on the input type of the editing field. + switchKeyboard(keyboardType) + } + Rime.getInstance() - // style/reset_ascii_mode指定了弹出键盘时是否重置ASCII状态。 - // 键盘的reset_ascii_mode指定了重置时是否重置到keyboard的ascii_mode描述的状态。 - if (shouldResetAsciiMode && KeyboardSwitcher.currentKeyboard.isResetAsciiMode) { - tempAsciiMode = KeyboardSwitcher.currentKeyboard.asciiMode - } - tempAsciiMode?.let { Rime.setOption("ascii_mode", it) } - isComposable = isComposable && !Rime.isEmpty - if (!trime.onEvaluateInputViewShown()) { - // Show candidate view when using physical keyboard - trime.setCandidatesViewShown(isComposable) + // style/reset_ascii_mode指定了弹出键盘时是否重置ASCII状态。 + // 键盘的reset_ascii_mode指定了重置时是否重置到keyboard的ascii_mode描述的状态。 + if (shouldResetAsciiMode && KeyboardSwitcher.currentKeyboard.isResetAsciiMode) { + tempAsciiMode = KeyboardSwitcher.currentKeyboard.asciiMode + } + tempAsciiMode?.let { Rime.setOption("ascii_mode", it) } + isComposable = isComposable && !Rime.isEmpty + if (!trime.onEvaluateInputViewShown()) { + // Show candidate view when using physical keyboard + trime.setCandidatesViewShown(isComposable) + } } - } - private fun handleRimeNotification(notification: RimeNotification) { - if (notification is RimeNotification.SchemaNotification) { - SchemaManager.init(notification.schemaId) - Rime.updateStatus() - trime.initKeyboard() - } else if (notification is RimeNotification.OptionNotification) { - Rime.updateContext() // 切換中英文、簡繁體時更新候選 - val value = notification.value - when (val option = notification.option) { - "ascii_mode" -> { - trime.inputFeedbackManager.ttsLanguage = - locales[if (value) 1 else 0] - } - "_hide_comment" -> trime.setShowComment(!value) - "_hide_candidate" -> { - candidateRoot?.visibility = if (!value) View.VISIBLE else View.GONE - trime.setCandidatesViewShown(isComposable && !value) - } - "_liquid_keyboard" -> trime.selectLiquidKeyboard(0) - "_hide_key_hint" -> mainKeyboardView?.setShowHint(!value) - "_hide_key_symbol" -> mainKeyboardView?.setShowSymbol(!value) - else -> if (option.startsWith("_keyboard_") && - option.length > 10 && value - ) { - val keyboard = option.substring(10) - KeyboardSwitcher.switchKeyboard(keyboard) - trime.bindKeyboardToInputView() - } else if (option.startsWith("_key_") && option.length > 5 && value) { - shouldUpdateRimeOption = false // 防止在 handleRimeNotification 中 setOption - val key = option.substring(5) - onEvent(Event(key)) - shouldUpdateRimeOption = true + private fun handleRimeNotification(notification: RimeNotification) { + if (notification is RimeNotification.SchemaNotification) { + SchemaManager.init(notification.schemaId) + Rime.updateStatus() + trime.initKeyboard() + } else if (notification is RimeNotification.OptionNotification) { + Rime.updateContext() // 切換中英文、簡繁體時更新候選 + val value = notification.value + when (val option = notification.option) { + "ascii_mode" -> { + trime.inputFeedbackManager.ttsLanguage = + locales[if (value) 1 else 0] + } + "_hide_comment" -> trime.setShowComment(!value) + "_hide_candidate" -> { + candidateRoot?.visibility = if (!value) View.VISIBLE else View.GONE + trime.setCandidatesViewShown(isComposable && !value) + } + "_liquid_keyboard" -> trime.selectLiquidKeyboard(0) + "_hide_key_hint" -> mainKeyboardView?.setShowHint(!value) + "_hide_key_symbol" -> mainKeyboardView?.setShowSymbol(!value) + else -> + if (option.startsWith("_keyboard_") && + option.length > 10 && value + ) { + val keyboard = option.substring(10) + KeyboardSwitcher.switchKeyboard(keyboard) + trime.bindKeyboardToInputView() + } else if (option.startsWith("_key_") && option.length > 5 && value) { + shouldUpdateRimeOption = false // 防止在 handleRimeNotification 中 setOption + val key = option.substring(5) + onEvent(Event(key)) + shouldUpdateRimeOption = true + } } + mainKeyboardView?.invalidateAllKeys() } - mainKeyboardView?.invalidateAllKeys() } - } - override fun onPress(keyEventCode: Int) { - trime.inputFeedbackManager?.let { - it.keyPressVibrate() - it.keyPressSound(keyEventCode) - it.keyPressSpeak(keyEventCode) + override fun onPress(keyEventCode: Int) { + trime.inputFeedbackManager?.let { + it.keyPressVibrate() + it.keyPressSound(keyEventCode) + it.keyPressSpeak(keyEventCode) + } } - } - override fun onRelease(keyEventCode: Int) { - Timber.d( - "\t\tonRelease() needSendUpRimeKey=" + needSendUpRimeKey + ", keyEventcode=" + keyEventCode + - ", Event.getRimeEvent=" + Event.getRimeEvent(keyEventCode, Rime.META_RELEASE_ON), - ) - if (needSendUpRimeKey) { - if (shouldUpdateRimeOption) { - Rime.setOption("soft_cursors", prefs.keyboard.softCursorEnabled) - Rime.setOption("_horizontal", Theme.get().style.getBoolean("horizontal")) - shouldUpdateRimeOption = false + override fun onRelease(keyEventCode: Int) { + Timber.d( + "\t\tonRelease() needSendUpRimeKey=" + needSendUpRimeKey + ", keyEventcode=" + keyEventCode + + ", Event.getRimeEvent=" + Event.getRimeEvent(keyEventCode, Rime.META_RELEASE_ON), + ) + if (needSendUpRimeKey) { + if (shouldUpdateRimeOption) { + Rime.setOption("soft_cursors", prefs.keyboard.softCursorEnabled) + Rime.setOption("_horizontal", Theme.get().style.getBoolean("horizontal")) + shouldUpdateRimeOption = false + } + // todo 释放按键可能不对 + val event = Event.getRimeEvent(keyEventCode, Rime.META_RELEASE_ON) + Rime.processKey(event[0], event[1]) + activeEditorInstance.commitRimeText() } - // todo 释放按键可能不对 - val event = Event.getRimeEvent(keyEventCode, Rime.META_RELEASE_ON) - Rime.processKey(event[0], event[1]) - activeEditorInstance.commitRimeText() + Timber.d("\t\tonRelease() finish") } - Timber.d("\t\tonRelease() finish") - } - // KeyboardEvent 处理软键盘事件 - override fun onEvent(event: Event?) { - event ?: return - if (!event.commit.isNullOrEmpty()) { - // Directly commit the text and don't dispatch to Rime - activeEditorInstance.commitText(event.commit, true) - return - } - if (!event.text.isNullOrEmpty()) { - onText(event.text) - return - } - when (event.code) { - KeyEvent.KEYCODE_SWITCH_CHARSET -> { // Switch status - Rime.toggleOption(event.toggle) - activeEditorInstance.commitRimeText() + // KeyboardEvent 处理软键盘事件 + override fun onEvent(event: Event?) { + event ?: return + if (!event.commit.isNullOrEmpty()) { + // Directly commit the text and don't dispatch to Rime + activeEditorInstance.commitText(event.commit, true) + return } - KeyEvent.KEYCODE_EISU -> { // Switch keyboard - KeyboardSwitcher.switchKeyboard(event.select) - /** Set ascii mode according to keyboard's settings, can not place into [Rime.handleRimeNotification] */ - if (shouldResetAsciiMode && KeyboardSwitcher.currentKeyboard.isResetAsciiMode) { - Rime.setOption("ascii_mode", KeyboardSwitcher.currentKeyboard.asciiMode) - } - trime.bindKeyboardToInputView() - trime.updateComposing() + if (!event.text.isNullOrEmpty()) { + onText(event.text) + return } - KeyEvent.KEYCODE_LANGUAGE_SWITCH -> { // Switch IME - when { - event.select!!.contentEquals(".next") -> { - trime.switchToNextIme() - } - event.select.isNotEmpty() -> { - trime.switchToPrevIme() - } - else -> { - inputMethodManager.showInputMethodPicker() + when (event.code) { + KeyEvent.KEYCODE_SWITCH_CHARSET -> { // Switch status + Rime.toggleOption(event.toggle) + activeEditorInstance.commitRimeText() + } + KeyEvent.KEYCODE_EISU -> { // Switch keyboard + KeyboardSwitcher.switchKeyboard(event.select) + /** Set ascii mode according to keyboard's settings, can not place into [Rime.handleRimeNotification] */ + if (shouldResetAsciiMode && KeyboardSwitcher.currentKeyboard.isResetAsciiMode) { + Rime.setOption("ascii_mode", KeyboardSwitcher.currentKeyboard.asciiMode) } + trime.bindKeyboardToInputView() + trime.updateComposing() } - } - KeyEvent.KEYCODE_FUNCTION -> { // Command Express - // Comments from trime.yaml: - // %s或者%1$s爲當前字符 - // %2$s爲當前輸入的編碼 - // %3$s爲光標前字符 - // %4$s爲光標前所有字符 - var arg = event.option - val activeTextRegex = Regex(".*%(\\d*)\\$" + "s.*") - if (arg.matches(activeTextRegex)) { - var activeTextMode = - arg.replaceFirst(activeTextRegex, "$1").toDouble().toInt() - if (activeTextMode < 1) { - activeTextMode = 1 + KeyEvent.KEYCODE_LANGUAGE_SWITCH -> { // Switch IME + when { + event.select!!.contentEquals(".next") -> { + trime.switchToNextIme() + } + event.select.isNotEmpty() -> { + trime.switchToPrevIme() + } + else -> { + inputMethodManager.showInputMethodPicker() + } } - val activeText = activeEditorInstance.getActiveText(activeTextMode) - arg = String.format( - arg, - activeEditorInstance.lastCommittedText, - Rime.getRimeRawInput() ?: "", - activeText, - activeText, - ) } + KeyEvent.KEYCODE_FUNCTION -> { // Command Express + // Comments from trime.yaml: + // %s或者%1$s爲當前字符 + // %2$s爲當前輸入的編碼 + // %3$s爲光標前字符 + // %4$s爲光標前所有字符 + var arg = event.option + val activeTextRegex = Regex(".*%(\\d*)\\$" + "s.*") + if (arg.matches(activeTextRegex)) { + var activeTextMode = + arg.replaceFirst(activeTextRegex, "$1").toDouble().toInt() + if (activeTextMode < 1) { + activeTextMode = 1 + } + val activeText = activeEditorInstance.getActiveText(activeTextMode) + arg = + String.format( + arg, + activeEditorInstance.lastCommittedText, + Rime.getRimeRawInput() ?: "", + activeText, + activeText, + ) + } - if (event.command == "liquid_keyboard") { - trime.selectLiquidKeyboard(arg) - } else if (event.command == "paste_by_char") { - trime.pasteByChar() - } else { - val textFromCommand = ShortcutUtils - .call(trime, event.command, arg) - if (textFromCommand != null) { - activeEditorInstance.commitText(textFromCommand) - trime.updateComposing() + if (event.command == "liquid_keyboard") { + trime.selectLiquidKeyboard(arg) + } else if (event.command == "paste_by_char") { + trime.pasteByChar() + } else { + val textFromCommand = + ShortcutUtils + .call(trime, event.command, arg) + if (textFromCommand != null) { + activeEditorInstance.commitText(textFromCommand) + trime.updateComposing() + } } } - } - KeyEvent.KEYCODE_VOICE_ASSIST -> Speech(trime).startListening() // Speech Recognition - KeyEvent.KEYCODE_SETTINGS -> { // Settings - trime.lifecycleScope.launch { - when (event.option) { - "theme" -> trime.showDialogAboveInputView( - trime.themePicker(Theme_AppCompat_DayNight_Dialog_Alert), - ) - "color" -> trime.showDialogAboveInputView( + KeyEvent.KEYCODE_VOICE_ASSIST -> Speech(trime).startListening() // Speech Recognition + KeyEvent.KEYCODE_SETTINGS -> { // Settings + trime.lifecycleScope.launch { + when (event.option) { + "theme" -> + trime.showDialogAboveInputView( + trime.themePicker(Theme_AppCompat_DayNight_Dialog_Alert), + ) + "color" -> + trime.showDialogAboveInputView( + trime.colorPicker(Theme_AppCompat_DayNight_Dialog_Alert), + ) + "schema" -> + trime.showDialogAboveInputView( + trime.schemaPicker(Theme_AppCompat_DayNight_Dialog_Alert), + ) + "sound" -> + trime.showDialogAboveInputView( + trime.soundPicker(Theme_AppCompat_DayNight_Dialog_Alert), + ) + else -> ShortcutUtils.launchMainActivity(trime) + } + } + } + KeyEvent.KEYCODE_PROG_RED -> + trime.lifecycleScope.launch { + trime.showDialogAboveInputView( trime.colorPicker(Theme_AppCompat_DayNight_Dialog_Alert), ) - "schema" -> trime.showDialogAboveInputView( - trime.schemaPicker(Theme_AppCompat_DayNight_Dialog_Alert), - ) - "sound" -> trime.showDialogAboveInputView( - trime.soundPicker(Theme_AppCompat_DayNight_Dialog_Alert), - ) - else -> ShortcutUtils.launchMainActivity(trime) } - } - } - KeyEvent.KEYCODE_PROG_RED -> trime.lifecycleScope.launch { - trime.showDialogAboveInputView( - trime.colorPicker(Theme_AppCompat_DayNight_Dialog_Alert), - ) - } - KeyEvent.KEYCODE_MENU -> showOptionsDialog() - else -> { - if (event.mask == 0 && KeyboardSwitcher.currentKeyboard.isOnlyShiftOn) { - if (event.code == KeyEvent.KEYCODE_SPACE && prefs.keyboard.hookShiftSpace) { - onKey(event.code, 0) - return - } else if (event.code >= KeyEvent.KEYCODE_0 && event.code <= KeyEvent.KEYCODE_9 && prefs.keyboard.hookShiftNum) { - onKey(event.code, 0) - return - } else if (prefs.keyboard.hookShiftSymbol) { - if (event.code >= KeyEvent.KEYCODE_GRAVE && event.code <= KeyEvent.KEYCODE_SLASH || - event.code == KeyEvent.KEYCODE_COMMA || - event.code == KeyEvent.KEYCODE_PERIOD - ) { + KeyEvent.KEYCODE_MENU -> showOptionsDialog() + else -> { + if (event.mask == 0 && KeyboardSwitcher.currentKeyboard.isOnlyShiftOn) { + if (event.code == KeyEvent.KEYCODE_SPACE && prefs.keyboard.hookShiftSpace) { + onKey(event.code, 0) + return + } else if (event.code >= KeyEvent.KEYCODE_0 && event.code <= KeyEvent.KEYCODE_9 && prefs.keyboard.hookShiftNum) { onKey(event.code, 0) return + } else if (prefs.keyboard.hookShiftSymbol) { + if (event.code >= KeyEvent.KEYCODE_GRAVE && event.code <= KeyEvent.KEYCODE_SLASH || + event.code == KeyEvent.KEYCODE_COMMA || + event.code == KeyEvent.KEYCODE_PERIOD + ) { + onKey(event.code, 0) + return + } } } - } - if (event.mask == 0) { - onKey(event.code, KeyboardSwitcher.currentKeyboard.modifer) - } else { - onKey(event.code, event.mask) + if (event.mask == 0) { + onKey(event.code, KeyboardSwitcher.currentKeyboard.modifer) + } else { + onKey(event.code, event.mask) + } } } } - } - override fun onKey(keyEventCode: Int, metaState: Int) { - printModifierKeyState(metaState, "keyEventCode=$keyEventCode") + override fun onKey( + keyEventCode: Int, + metaState: Int, + ) { + printModifierKeyState(metaState, "keyEventCode=$keyEventCode") - // 优先由librime处理按键事件 - if (trime.handleKey(keyEventCode, metaState)) return + // 优先由librime处理按键事件 + if (trime.handleKey(keyEventCode, metaState)) return - needSendUpRimeKey = false + needSendUpRimeKey = false - // 如果没有修饰键,或者只有shift修饰键,针对非Android标准按键,可以直接commit字符 - if ((metaState == KeyEvent.META_SHIFT_ON || metaState == 0) && keyEventCode >= Keycode.A.ordinal) { - val text = Keycode.getSymbolLabel(Keycode.valueOf(keyEventCode)) - if (text.length == 1) { - activeEditorInstance.commitText(text) + // 如果没有修饰键,或者只有shift修饰键,针对非Android标准按键,可以直接commit字符 + if ((metaState == KeyEvent.META_SHIFT_ON || metaState == 0) && keyEventCode >= Keycode.A.ordinal) { + val text = Keycode.getSymbolLabel(Keycode.valueOf(keyEventCode)) + if (text.length == 1) { + activeEditorInstance.commitText(text) + return + } + } + // 小键盘自动增加锁定 + if (keyEventCode >= KeyEvent.KEYCODE_NUMPAD_0 && keyEventCode <= KeyEvent.KEYCODE_NUMPAD_EQUALS) { + activeEditorInstance.sendDownUpKeyEvent(keyEventCode, metaState or KeyEvent.META_NUM_LOCK_ON) return } + // 大写字母和部分符号转换为Shift+Android keyevent + val event = toStdKeyEvent(keyEventCode, metaState) + activeEditorInstance.sendDownUpKeyEvent(event[0], event[1]) } - // 小键盘自动增加锁定 - if (keyEventCode >= KeyEvent.KEYCODE_NUMPAD_0 && keyEventCode <= KeyEvent.KEYCODE_NUMPAD_EQUALS) { - activeEditorInstance.sendDownUpKeyEvent(keyEventCode, metaState or KeyEvent.META_NUM_LOCK_ON) - return - } - // 大写字母和部分符号转换为Shift+Android keyevent - val event = toStdKeyEvent(keyEventCode, metaState) - activeEditorInstance.sendDownUpKeyEvent(event[0], event[1]) - } - override fun onText(text: CharSequence?) { - text ?: return - if (!text.startsWithAsciiChar() && Rime.isComposing) { - Rime.commitComposition() - activeEditorInstance.commitRimeText() - } - var textToParse = text - while (textToParse!!.isNotEmpty()) { - var target: String - val escapeTagMatcher = DELIMITER_PROPERTY_KEY.toPattern().matcher(textToParse) - val propertyGroupMatcher = DELIMITER_PROPERTY_GROUP.toPattern().matcher(textToParse) - when { - escapeTagMatcher.matches() -> { - target = escapeTagMatcher.group(1) ?: "" - Rime.simulateKeySequence(target) - if (!activeEditorInstance.commitRimeText() && !Rime.isComposing) { - activeEditorInstance.commitText(target) + override fun onText(text: CharSequence?) { + text ?: return + if (!text.startsWithAsciiChar() && Rime.isComposing) { + Rime.commitComposition() + activeEditorInstance.commitRimeText() + } + var textToParse = text + while (textToParse!!.isNotEmpty()) { + var target: String + val escapeTagMatcher = DELIMITER_PROPERTY_KEY.toPattern().matcher(textToParse) + val propertyGroupMatcher = DELIMITER_PROPERTY_GROUP.toPattern().matcher(textToParse) + when { + escapeTagMatcher.matches() -> { + target = escapeTagMatcher.group(1) ?: "" + Rime.simulateKeySequence(target) + if (!activeEditorInstance.commitRimeText() && !Rime.isComposing) { + activeEditorInstance.commitText(target) + } + trime.updateComposing() + } + propertyGroupMatcher.matches() -> { + target = propertyGroupMatcher.group(1) ?: "" + onEvent(Event(target)) + } + else -> { + target = textToParse.substring(0, 1) + onEvent(Event(target)) } - trime.updateComposing() - } - propertyGroupMatcher.matches() -> { - target = propertyGroupMatcher.group(1) ?: "" - onEvent(Event(target)) - } - else -> { - target = textToParse.substring(0, 1) - onEvent(Event(target)) } + textToParse = textToParse.substring(target.length) } - textToParse = textToParse.substring(target.length) + needSendUpRimeKey = false } - needSendUpRimeKey = false - } - /** - * Commits the pressed candidate and suggest the following words. - */ - override fun onCandidatePressed(index: Int) { - onPress(0) - if (!Rime.isComposing) { - if (index >= 0) { - SchemaManager.toggleSwitchOption(index) - trime.updateComposing() - } - } else if (prefs.keyboard.hookCandidate || index > 9) { - if (Rime.selectCandidate(index)) { - if (prefs.keyboard.hookCandidateCommit) { - // todo 找到切换高亮候选词的API,并把此处改为模拟移动候选后发送空格 - // 如果使用了lua处理候选上屏,模拟数字键、空格键是非常有必要的 - activeEditorInstance.commitRimeText() - } else { - activeEditorInstance.commitRimeText() + /** + * Commits the pressed candidate and suggest the following words. + */ + override fun onCandidatePressed(index: Int) { + onPress(0) + if (!Rime.isComposing) { + if (index >= 0) { + SchemaManager.toggleSwitchOption(index) + trime.updateComposing() + } + } else if (prefs.keyboard.hookCandidate || index > 9) { + if (Rime.selectCandidate(index)) { + if (prefs.keyboard.hookCandidateCommit) { + // todo 找到切换高亮候选词的API,并把此处改为模拟移动候选后发送空格 + // 如果使用了lua处理候选上屏,模拟数字键、空格键是非常有必要的 + activeEditorInstance.commitRimeText() + } else { + activeEditorInstance.commitRimeText() + } } + } else if (index == 9) { + trime.handleKey(KeyEvent.KEYCODE_0, 0) + } else { + trime.handleKey(KeyEvent.KEYCODE_1 + index, 0) } - } else if (index == 9) { - trime.handleKey(KeyEvent.KEYCODE_0, 0) - } else { - trime.handleKey(KeyEvent.KEYCODE_1 + index, 0) } - } - override fun onCandidateSymbolPressed(arrow: String) { - when (arrow) { - Candidate.PAGE_UP_BUTTON -> onKey(KeyEvent.KEYCODE_PAGE_UP, 0) - Candidate.PAGE_DOWN_BUTTON -> onKey(KeyEvent.KEYCODE_PAGE_DOWN, 0) - Candidate.PAGE_EX_BUTTON -> Trime.getService().selectLiquidKeyboard(SymbolKeyboardType.CANDIDATE) + override fun onCandidateSymbolPressed(arrow: String) { + when (arrow) { + Candidate.PAGE_UP_BUTTON -> onKey(KeyEvent.KEYCODE_PAGE_UP, 0) + Candidate.PAGE_DOWN_BUTTON -> onKey(KeyEvent.KEYCODE_PAGE_DOWN, 0) + Candidate.PAGE_EX_BUTTON -> Trime.getService().selectLiquidKeyboard(SymbolKeyboardType.CANDIDATE) + } } - } - override fun onCandidateLongClicked(index: Int) { - Rime.deleteCandidate(index) - trime.updateComposing() - } + override fun onCandidateLongClicked(index: Int) { + Rime.deleteCandidate(index) + trime.updateComposing() + } - private fun showOptionsDialog() { - val builder = AlertDialog.Builder(trime, Theme_AppCompat_DayNight_Dialog_Alert) - builder - .setTitle(R.string.app_name_release) - .setIcon(R.mipmap.ic_app_icon) - .setNegativeButton(R.string.other_ime) { dialog, _ -> - dialog.dismiss() - inputMethodManager.showInputMethodPicker() - } - .setPositiveButton(R.string.set_ime) { dialog, _ -> - ShortcutUtils.launchMainActivity(trime) - dialog.dismiss() - } - if (Rime.getCurrentRimeSchema() == (".default")) { - builder.setMessage(R.string.no_schemas) - } else { - val schemaList = Rime.getRimeSchemaList() - val schemaNameList = schemaList.map(SchemaListItem::name).toTypedArray() - val schemaIdList = schemaList.map(SchemaListItem::schemaId).toTypedArray() - val currentSchema = Rime.getCurrentRimeSchema() + private fun showOptionsDialog() { + val builder = AlertDialog.Builder(trime, Theme_AppCompat_DayNight_Dialog_Alert) builder - .setNegativeButton( - R.string.pref_select_schemas, - ) { dialog, _ -> + .setTitle(R.string.app_name_release) + .setIcon(R.mipmap.ic_app_icon) + .setNegativeButton(R.string.other_ime) { dialog, _ -> dialog.dismiss() - trime.showDialogAboveInputView(trime.schemaPicker(Theme_AppCompat_DayNight_Dialog_Alert)) + inputMethodManager.showInputMethodPicker() } - .setSingleChoiceItems( - schemaNameList, - schemaIdList.indexOf(currentSchema), - ) { dialog: DialogInterface, id: Int -> + .setPositiveButton(R.string.set_ime) { dialog, _ -> + ShortcutUtils.launchMainActivity(trime) dialog.dismiss() - trime.lifecycleScope.launch { - Rime.selectSchema(schemaIdList[id] ?: return@launch) - } - shouldUpdateRimeOption = true } + if (Rime.getCurrentRimeSchema() == (".default")) { + builder.setMessage(R.string.no_schemas) + } else { + val schemaList = Rime.getRimeSchemaList() + val schemaNameList = schemaList.map(SchemaListItem::name).toTypedArray() + val schemaIdList = schemaList.map(SchemaListItem::schemaId).toTypedArray() + val currentSchema = Rime.getCurrentRimeSchema() + builder + .setNegativeButton( + R.string.pref_select_schemas, + ) { dialog, _ -> + dialog.dismiss() + trime.showDialogAboveInputView(trime.schemaPicker(Theme_AppCompat_DayNight_Dialog_Alert)) + } + .setSingleChoiceItems( + schemaNameList, + schemaIdList.indexOf(currentSchema), + ) { dialog: DialogInterface, id: Int -> + dialog.dismiss() + trime.lifecycleScope.launch { + Rime.selectSchema(schemaIdList[id] ?: return@launch) + } + shouldUpdateRimeOption = true + } + } + trime.showDialogAboveInputView(builder.create()) } - trime.showDialogAboveInputView(builder.create()) } -} diff --git a/app/src/main/java/com/osfans/trime/ui/components/CoroutineChoiceDialog.kt b/app/src/main/java/com/osfans/trime/ui/components/CoroutineChoiceDialog.kt index c2ea3b9c34..f1463a4e6a 100644 --- a/app/src/main/java/com/osfans/trime/ui/components/CoroutineChoiceDialog.kt +++ b/app/src/main/java/com/osfans/trime/ui/components/CoroutineChoiceDialog.kt @@ -43,13 +43,15 @@ class CoroutineChoiceDialog( return this } - private suspend fun init() = withContext(initDispatcher) { - onInitListener.onAction() - } + private suspend fun init() = + withContext(initDispatcher) { + onInitListener.onAction() + } - private suspend fun positive() = withContext(postiveDispatcher) { - onPositiveListener.onAction() - } + private suspend fun positive() = + withContext(postiveDispatcher) { + onPositiveListener.onAction() + } private fun build() { with(builder) { diff --git a/app/src/main/java/com/osfans/trime/ui/components/DialogSeekBarPreference.kt b/app/src/main/java/com/osfans/trime/ui/components/DialogSeekBarPreference.kt index d831b0de23..5956dd23f9 100644 --- a/app/src/main/java/com/osfans/trime/ui/components/DialogSeekBarPreference.kt +++ b/app/src/main/java/com/osfans/trime/ui/components/DialogSeekBarPreference.kt @@ -83,38 +83,47 @@ class DialogSeekBarPreference : Preference { /** * Shows the seek bar dialog. */ - private fun showSeekBarDialog() = with(context) { - val initValue = currentValue - val dialogView = SeekBarDialogBinding.inflate(LayoutInflater.from(this)) - dialogView.textView.text = getTextForValue(initValue) - dialogView.seekBar.apply { - max = getProgressForValue(this@DialogSeekBarPreference.max) - progress = getProgressForValue(initValue) - setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { - override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { - dialogView.textView.text = getTextForValue(getValueForProgress(progress)) + private fun showSeekBarDialog() = + with(context) { + val initValue = currentValue + val dialogView = SeekBarDialogBinding.inflate(LayoutInflater.from(this)) + dialogView.textView.text = getTextForValue(initValue) + dialogView.seekBar.apply { + max = getProgressForValue(this@DialogSeekBarPreference.max) + progress = getProgressForValue(initValue) + setOnSeekBarChangeListener( + object : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged( + seekBar: SeekBar?, + progress: Int, + fromUser: Boolean, + ) { + dialogView.textView.text = getTextForValue(getValueForProgress(progress)) + } + + override fun onStartTrackingTouch(seekBar: SeekBar?) {} + + override fun onStopTrackingTouch(seekBar: SeekBar?) {} + }, + ) + } + AlertDialog.Builder(this) + .setTitle(this@DialogSeekBarPreference.title) + .setView(dialogView.root) + .setPositiveButton(android.R.string.ok) { _, _ -> + val actualValue = getValueForProgress(dialogView.seekBar.progress) + if (callChangeListener(actualValue)) { + persistInt(actualValue) + notifyChanged() + } } - override fun onStartTrackingTouch(seekBar: SeekBar?) {} - override fun onStopTrackingTouch(seekBar: SeekBar?) {} - }) - } - AlertDialog.Builder(this) - .setTitle(this@DialogSeekBarPreference.title) - .setView(dialogView.root) - .setPositiveButton(android.R.string.ok) { _, _ -> - val actualValue = getValueForProgress(dialogView.seekBar.progress) - if (callChangeListener(actualValue)) { - persistInt(actualValue) + .setNeutralButton(R.string.pref__default) { _, _ -> + persistInt(defaultValue) notifyChanged() } - } - .setNeutralButton(R.string.pref__default) { _, _ -> - persistInt(defaultValue) - notifyChanged() - } - .setNegativeButton(android.R.string.cancel, null) - .show() - } + .setNegativeButton(android.R.string.cancel, null) + .show() + } /** * Converts the actual value to a progress value which the Android SeekBar implementation can diff --git a/app/src/main/java/com/osfans/trime/ui/components/FolderPickerPreference.kt b/app/src/main/java/com/osfans/trime/ui/components/FolderPickerPreference.kt index 79097dce22..a0aa7fbb92 100644 --- a/app/src/main/java/com/osfans/trime/ui/components/FolderPickerPreference.kt +++ b/app/src/main/java/com/osfans/trime/ui/components/FolderPickerPreference.kt @@ -37,7 +37,10 @@ class FolderPickerPreference : Preference { private val currentValue: String get() = getPersistedString(value) - override fun onGetDefaultValue(a: TypedArray, index: Int): Any { + override fun onGetDefaultValue( + a: TypedArray, + index: Int, + ): Any { return a.getString(index) ?: "" } diff --git a/app/src/main/java/com/osfans/trime/ui/components/log/LogAdapter.kt b/app/src/main/java/com/osfans/trime/ui/components/log/LogAdapter.kt index c74462ef29..e259972082 100644 --- a/app/src/main/java/com/osfans/trime/ui/components/log/LogAdapter.kt +++ b/app/src/main/java/com/osfans/trime/ui/components/log/LogAdapter.kt @@ -18,7 +18,6 @@ import splitties.dimensions.dp */ class LogAdapter(private val entries: MutableList = mutableListOf()) : RecyclerView.Adapter() { - inner class ViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView) fun append(line: CharSequence) { @@ -37,7 +36,10 @@ class LogAdapter(private val entries: MutableList = mutableListOf( override fun getItemCount() = entries.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ViewHolder { return ViewHolder( TextView(parent.context).apply { textSize = 12f @@ -45,15 +47,19 @@ class LogAdapter(private val entries: MutableList = mutableListOf( if (Build.VERSION.SDK_INT >= VERSION_CODES.O) { setTextClassifier(TextClassifier.NO_OP) } - layoutParams = MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT).apply { - marginStart = dp(4) - marginEnd = dp(4) - } + layoutParams = + MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT).apply { + marginStart = dp(4) + marginEnd = dp(4) + } }, ) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { holder.textView.text = entries[position] } } diff --git a/app/src/main/java/com/osfans/trime/ui/components/log/LogView.kt b/app/src/main/java/com/osfans/trime/ui/components/log/LogView.kt index efe6c684b7..b2a7f634b0 100644 --- a/app/src/main/java/com/osfans/trime/ui/components/log/LogView.kt +++ b/app/src/main/java/com/osfans/trime/ui/components/log/LogView.kt @@ -24,81 +24,85 @@ import splitties.resources.styledColor * Source: * [fcitx5-android/LogView](https://github.com/fcitx5-android/fcitx5-android/blob/5ac719c3547165a3e77fe265c471a5a211580320/app/src/main/java/org/fcitx/fcitx5/android/ui/main/log/LogView.kt) */ -class LogView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null) : +class LogView + @JvmOverloads + constructor(context: Context, attributeSet: AttributeSet? = null) : HorizontalScrollView(context, attributeSet) { + private var logcat: Logcat? = null - private var logcat: Logcat? = null + private val logAdapter = LogAdapter() - private val logAdapter = LogAdapter() + private val recyclerView = + RecyclerView(context).apply { + adapter = logAdapter + layoutManager = + LinearLayoutManager(context).apply { + orientation = LinearLayoutManager.VERTICAL + } + } - private val recyclerView = RecyclerView(context).apply { - adapter = logAdapter - layoutManager = LinearLayoutManager(context).apply { - orientation = LinearLayoutManager.VERTICAL + init { + addView( + recyclerView, + LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ), + ) } - } - - init { - addView( - recyclerView, - LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.MATCH_PARENT, - ), - ) - } - override fun onDetachedFromWindow() { - logcat?.shutdownLogFlow() - super.onDetachedFromWindow() - } + override fun onDetachedFromWindow() { + logcat?.shutdownLogFlow() + super.onDetachedFromWindow() + } - fun fromCustomLogLines(lines: List) { - lines.onEach { - dyeAndAppendString(it) + fun fromCustomLogLines(lines: List) { + lines.onEach { + dyeAndAppendString(it) + } } - } - fun append(content: String) { - logAdapter.append( - buildSpannedString { - color(styledColor(android.R.attr.colorForeground)) { append(content) } - }, - ) - } + fun append(content: String) { + logAdapter.append( + buildSpannedString { + color(styledColor(android.R.attr.colorForeground)) { append(content) } + }, + ) + } - fun setLogcat(logcat: Logcat) { - this.logcat = logcat - logcat.initLogFlow() - logcat.logFlow.onEach { - dyeAndAppendString(it) - }.launchIn(findViewTreeLifecycleOwner()!!.lifecycleScope) - } + fun setLogcat(logcat: Logcat) { + this.logcat = logcat + logcat.initLogFlow() + logcat.logFlow.onEach { + dyeAndAppendString(it) + }.launchIn(findViewTreeLifecycleOwner()!!.lifecycleScope) + } - private fun dyeAndAppendString(str: String) { - val color = ContextCompat.getColor( - context, - when (str.first()) { - 'V' -> R.color.grey_700 - 'D' -> R.color.grey_700 - 'I' -> R.color.blue_500 - 'W' -> R.color.yellow_800 - 'E' -> R.color.red_400 - 'F' -> R.color.red_A700 - else -> R.color.colorPrimary - }, - ) - logAdapter.append( - buildSpannedString { - color(color) { append(str) } - }, - ) - } + private fun dyeAndAppendString(str: String) { + val color = + ContextCompat.getColor( + context, + when (str.first()) { + 'V' -> R.color.grey_700 + 'D' -> R.color.grey_700 + 'I' -> R.color.blue_500 + 'W' -> R.color.yellow_800 + 'E' -> R.color.red_400 + 'F' -> R.color.red_A700 + else -> R.color.colorPrimary + }, + ) + logAdapter.append( + buildSpannedString { + color(color) { append(str) } + }, + ) + } - val currentLog: String - get() = logAdapter.fullLogString() + val currentLog: String + get() = logAdapter.fullLogString() - fun clear() { - logAdapter.clear() + fun clear() { + logAdapter.clear() + } } -} diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/AboutFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/AboutFragment.kt index 8ee88a4f23..5680d145d1 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/AboutFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/AboutFragment.kt @@ -23,16 +23,20 @@ import splitties.systemservices.clipboardManager class AboutFragment : PaddingPreferenceFragment() { private val viewModel: MainViewModel by activityViewModels() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.about_preference, rootKey) with(preferenceScreen) { get("about__changelog")?.apply { summary = Const.displayVersionName isCopyingEnabled = true - intent = Intent( - Intent.ACTION_VIEW, - Uri.parse("${Const.currentGitRepo}/commits/${Const.buildGitHash}"), - ) + intent = + Intent( + Intent.ACTION_VIEW, + Uri.parse("${Const.currentGitRepo}/commits/${Const.buildGitHash}"), + ) } get("about__buildinfo")?.apply { summary = BuildConfig.BUILD_INFO diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/ClipboardFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/ClipboardFragment.kt index 638ef2bc7b..943948e3e5 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/ClipboardFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/ClipboardFragment.kt @@ -7,10 +7,12 @@ import com.osfans.trime.ui.components.PaddingPreferenceFragment import com.osfans.trime.ui.main.MainViewModel class ClipboardFragment : PaddingPreferenceFragment() { - private val viewModel: MainViewModel by activityViewModels() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { addPreferencesFromResource(R.xml.clipboard_preference) } diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/KeyboardFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/KeyboardFragment.kt index 175612f999..c392a3f80c 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/KeyboardFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/KeyboardFragment.kt @@ -21,7 +21,11 @@ class KeyboardFragment : PaddingPreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener { private val viewModel: MainViewModel by activityViewModels() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { addPreferencesFromResource(R.xml.keyboard_preference) findPreference("keyboard__key_sound_package") ?.setOnPreferenceClickListener { @@ -30,7 +34,10 @@ class KeyboardFragment : } } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + override fun onSharedPreferenceChanged( + sharedPreferences: SharedPreferences?, + key: String?, + ) { val trime = Trime.getServiceOrNull() when (key) { "keyboard__key_long_press_timeout", diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/LicenseFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/LicenseFragment.kt index 76d4cf28de..fb79781822 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/LicenseFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/LicenseFragment.kt @@ -16,10 +16,12 @@ import com.osfans.trime.ui.main.MainViewModel import kotlinx.coroutines.launch class LicenseFragment : PaddingPreferenceFragment() { - private val viewModel: MainViewModel by activityViewModels() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { lifecycleScope.launch { val context = preferenceManager.context val screen = preferenceManager.createPreferenceScreen(context) @@ -52,7 +54,10 @@ class LicenseFragment : PaddingPreferenceFragment() { viewModel.disableTopOptionsMenu() } - private fun showLicenseDialog(uniqueId: String, licenses: Set): Boolean { + private fun showLicenseDialog( + uniqueId: String, + licenses: Set, + ): Boolean { when (licenses.size) { 0 -> {} 1 -> showLicenseContent(licenses.first()) diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/OtherFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/OtherFragment.kt index be2823e5df..2eed4f5ff1 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/OtherFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/OtherFragment.kt @@ -15,15 +15,20 @@ import com.osfans.trime.ui.main.MainViewModel class OtherFragment : PaddingPreferenceFragment() { private val viewModel: MainViewModel by activityViewModels() private val prefs get() = AppPrefs.defaultInstance() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { addPreferencesFromResource(R.xml.other_preference) findPreference("other__ui_mode")?.setOnPreferenceChangeListener { _, newValue -> - val uiMode = when (newValue) { - "auto" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - "light" -> AppCompatDelegate.MODE_NIGHT_NO - "dark" -> AppCompatDelegate.MODE_NIGHT_YES - else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED - } + val uiMode = + when (newValue) { + "auto" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + "light" -> AppCompatDelegate.MODE_NIGHT_NO + "dark" -> AppCompatDelegate.MODE_NIGHT_YES + else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED + } AppCompatDelegate.setDefaultNightMode(uiMode) true } diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/PrefFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/PrefFragment.kt index 91306fecb6..b3c1dfc7f9 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/PrefFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/PrefFragment.kt @@ -13,7 +13,6 @@ import com.osfans.trime.ui.main.schemaPicker import kotlinx.coroutines.launch class PrefFragment : PaddingPreferenceFragment() { - private val viewModel: MainViewModel by activityViewModels() override fun onResume() { @@ -27,7 +26,10 @@ class PrefFragment : PaddingPreferenceFragment() { super.onPause() } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { setPreferencesFromResource(R.xml.prefs, rootKey) with(preferenceScreen) { get("pref_schemas")?.setOnPreferenceClickListener { diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt index 73a768ab9a..463cd2a58a 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt @@ -38,22 +38,26 @@ import java.util.concurrent.TimeUnit class ProfileFragment : PaddingPreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener { - private val viewModel: MainViewModel by activityViewModels() private val prefs get() = AppPrefs.defaultInstance() private fun FolderPickerPreference.registerDocumentTreeLauncher() { - documentTreeLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { - it ?: return@registerForActivityResult - val uri = DocumentsContract.buildDocumentUriUsingTree( - it, - DocumentsContract.getTreeDocumentId(it), - ) - dialogView.editText.setText(UriUtils.uri2File(uri).absolutePath) - } + documentTreeLauncher = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { + it ?: return@registerForActivityResult + val uri = + DocumentsContract.buildDocumentUriUsingTree( + it, + DocumentsContract.getTreeDocumentId(it), + ) + dialogView.editText.setText(UriUtils.uri2File(uri).absolutePath) + } } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { addPreferencesFromResource(R.xml.profile_preference) with(preferenceScreen) { get("profile_shared_data_dir")?.apply { @@ -110,16 +114,18 @@ class ProfileFragment : get("profile_timing_sync")?.setOnPreferenceClickListener { // 监听定时同步偏好设置 val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val pendingIntent = PendingIntent.getBroadcast( // 设置待发送的同步事件 - context, - 0, - Intent("com.osfans.trime.timing.sync"), - if (VERSION.SDK_INT >= VERSION_CODES.M) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) + // 设置待发送的同步事件 + val pendingIntent = + PendingIntent.getBroadcast( + context, + 0, + Intent("com.osfans.trime.timing.sync"), + if (VERSION.SDK_INT >= VERSION_CODES.M) { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + } else { + PendingIntent.FLAG_UPDATE_CURRENT + }, + ) val cal = Calendar.getInstance() if (get("profile_timing_sync")?.isChecked == true) { // 当定时同步偏好打开时 val timeSetListener = // 监听时间选择器设置 @@ -160,13 +166,15 @@ class ProfileFragment : } } } - val tpDialog = TimePickerDialog( // 时间选择器设置 - context, - timeSetListener, - cal.get(Calendar.HOUR_OF_DAY), - cal.get(Calendar.MINUTE), - true, - ) + // 时间选择器设置 + val tpDialog = + TimePickerDialog( + context, + timeSetListener, + cal.get(Calendar.HOUR_OF_DAY), + cal.get(Calendar.MINUTE), + true, + ) tpDialog.setOnCancelListener { // 当取消时间选择器时重置偏好 get("profile_timing_sync")?.isChecked = false } @@ -198,7 +206,7 @@ class ProfileFragment : "${DataManager.sharedDataDir.absolutePath}/$a", ) }.getOrNull() ?: false - ) + ) } } } @@ -212,7 +220,10 @@ class ProfileFragment : } } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { // 实时更新定时同步偏好描述 + override fun onSharedPreferenceChanged( + sharedPreferences: SharedPreferences?, + key: String?, + ) { // 实时更新定时同步偏好描述 val timingSyncPreference: SwitchPreferenceCompat? = findPreference("profile_timing_sync") when (key) { "profile_timing_sync_trigger_time", @@ -231,6 +242,7 @@ class ProfileFragment : } } } + override fun onResume() { super.onResume() viewModel.setToolbarTitle(getString(R.string.pref_profile)) diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/ThemeColorFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/ThemeColorFragment.kt index 146814df21..62f5636c8a 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/ThemeColorFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/ThemeColorFragment.kt @@ -15,7 +15,10 @@ import kotlinx.coroutines.launch class ThemeColorFragment : PaddingPreferenceFragment() { private val viewModel: MainViewModel by activityViewModels() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { addPreferencesFromResource(R.xml.theme_color_preference) with(preferenceScreen) { get("theme_selected_theme")?.setOnPreferenceClickListener { diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/ToolkitFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/ToolkitFragment.kt index d55cf4f1b7..72a9f32c8a 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/ToolkitFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/ToolkitFragment.kt @@ -9,10 +9,12 @@ import com.osfans.trime.ui.main.MainViewModel import com.osfans.trime.util.ShortcutUtils class ToolkitFragment : PaddingPreferenceFragment() { - private val viewModel: MainViewModel by activityViewModels() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences( + savedInstanceState: Bundle?, + rootKey: String?, + ) { val context = preferenceManager.context val screen = preferenceManager.createPreferenceScreen(context) screen.addPreference( diff --git a/app/src/main/java/com/osfans/trime/ui/main/LiquidKeyboardEditActivity.kt b/app/src/main/java/com/osfans/trime/ui/main/LiquidKeyboardEditActivity.kt index d862c61254..1cbe394370 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/LiquidKeyboardEditActivity.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/LiquidKeyboardEditActivity.kt @@ -24,14 +24,15 @@ class LiquidKeyboardEditActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) window.attributes.gravity = Gravity.TOP - val binding = ActivityLiquidKeyboardEditBinding.inflate(layoutInflater).apply { - editText = liquidKeyboardEditText - liquidKeyboardEditCancel.setOnClickListener { finish() } - liquidKeyboardEditOk.setOnClickListener { - editHandler() - finish() + val binding = + ActivityLiquidKeyboardEditBinding.inflate(layoutInflater).apply { + editText = liquidKeyboardEditText + liquidKeyboardEditCancel.setOnClickListener { finish() } + liquidKeyboardEditOk.setOnClickListener { + editHandler() + finish() + } } - } setContentView(binding.root) processIntent(intent) } diff --git a/app/src/main/java/com/osfans/trime/ui/main/LogActivity.kt b/app/src/main/java/com/osfans/trime/ui/main/LogActivity.kt index f9847482e5..fff0472444 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/LogActivity.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/LogActivity.kt @@ -31,7 +31,6 @@ import kotlinx.coroutines.launch * Source: [fcitx5-android/LogActivity](https://github.com/fcitx5-android/fcitx5-android/blob/24457e13b7c3f9f59a6f220db7caad3d02f27651/app/src/main/java/org/fcitx/fcitx5/android/ui/main/LogActivity.kt) */ class LogActivity : AppCompatActivity() { - private lateinit var launcher: ActivityResultLauncher private lateinit var logView: LogView @@ -41,18 +40,19 @@ class LogActivity : AppCompatActivity() { } private fun registerLauncher() { - launcher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri -> - lifecycleScope.launch(NonCancellable + Dispatchers.IO) { - uri?.runCatching { - contentResolver.openOutputStream(this)?.use { os -> - os.bufferedWriter().use { - it.write(DeviceInfo.get(this@LogActivity)) - it.write(logView.currentLog) + launcher = + registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri -> + lifecycleScope.launch(NonCancellable + Dispatchers.IO) { + uri?.runCatching { + contentResolver.openOutputStream(this)?.use { os -> + os.bufferedWriter().use { + it.write(DeviceInfo.get(this@LogActivity)) + it.write(logView.currentLog) + } } - } - }?.toast() + }?.toast() + } } - } } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt b/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt index c9a921a7f9..fa08f5401b 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class MainViewModel : ViewModel() { - val toolbarTitle = MutableLiveData() val topOptionsMenu = MutableLiveData() diff --git a/app/src/main/java/com/osfans/trime/ui/main/Pickers.kt b/app/src/main/java/com/osfans/trime/ui/main/Pickers.kt index 223aac893f..5d5e0de8c3 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/Pickers.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/Pickers.kt @@ -30,9 +30,10 @@ suspend fun Context.themePicker( title = getString(R.string.looks__selected_theme_title) initDispatcher = Dispatchers.IO onInit { - items = ThemeManager.getAllThemes() - .map { it.substringBeforeLast('.') } - .toTypedArray() + items = + ThemeManager.getAllThemes() + .map { it.substringBeforeLast('.') } + .toTypedArray() val current = ThemeManager.getActiveTheme().substringBeforeLast('.') checkedItem = items.indexOf(current) } @@ -98,10 +99,11 @@ fun Context.schemaPicker( .toTypedArray(), ) val loading = ProgressBarDialogIndeterminate(titleId = R.string.deploy_progress).create() - val job = launch { - delay(200L) - loading.show() - } + val job = + launch { + delay(200L) + loading.show() + } withContext(Dispatchers.Default) { Rime.deploy() job.cancelAndJoin() diff --git a/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt b/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt index 7f37734ea6..d9225ea67a 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt @@ -52,12 +52,13 @@ class PrefMainActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - val uiMode = when (prefs.other.uiMode) { - "auto" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - "light" -> AppCompatDelegate.MODE_NIGHT_NO - "dark" -> AppCompatDelegate.MODE_NIGHT_YES - else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED - } + val uiMode = + when (prefs.other.uiMode) { + "auto" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + "light" -> AppCompatDelegate.MODE_NIGHT_NO + "dark" -> AppCompatDelegate.MODE_NIGHT_YES + else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED + } AppCompatDelegate.setDefaultNightMode(uiMode) super.onCreate(savedInstanceState) @@ -82,10 +83,11 @@ class PrefMainActivity : AppCompatActivity() { setContentView(binding.root) setSupportActionBar(binding.prefToolbar.toolbar) - val appBarConfiguration = AppBarConfiguration( - topLevelDestinationIds = setOf(), - fallbackOnNavigateUpListener = ::onNavigateUpListener, - ) + val appBarConfiguration = + AppBarConfiguration( + topLevelDestinationIds = setOf(), + fallbackOnNavigateUpListener = ::onNavigateUpListener, + ) navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment binding.prefToolbar.toolbar.setupWithNavController(navHostFragment.navController, appBarConfiguration) @@ -151,25 +153,33 @@ class PrefMainActivity : AppCompatActivity() { private fun requestExternalStoragePermission() { XXPermissions.with(this) .permission(Permission.MANAGE_EXTERNAL_STORAGE) - .request(object : OnPermissionCallback { - override fun onGranted(permissions: List, all: Boolean) { - if (all) { - ToastUtils.showShort(R.string.external_storage_permission_granted) - SoundThemeManager.init() + .request( + object : OnPermissionCallback { + override fun onGranted( + permissions: List, + all: Boolean, + ) { + if (all) { + ToastUtils.showShort(R.string.external_storage_permission_granted) + SoundThemeManager.init() + } } - } - override fun onDenied(permissions: List, never: Boolean) { - if (never) { - ToastUtils.showShort(R.string.external_storage_permission_denied) - XXPermissions.startPermissionActivity( - this@PrefMainActivity, - permissions, - ) - } else { - ToastUtils.showShort(R.string.external_storage_permission_denied) + override fun onDenied( + permissions: List, + never: Boolean, + ) { + if (never) { + ToastUtils.showShort(R.string.external_storage_permission_denied) + XXPermissions.startPermissionActivity( + this@PrefMainActivity, + permissions, + ) + } else { + ToastUtils.showShort(R.string.external_storage_permission_denied) + } } - } - }) + }, + ) } } diff --git a/app/src/main/java/com/osfans/trime/ui/setup/SetupActivity.kt b/app/src/main/java/com/osfans/trime/ui/setup/SetupActivity.kt index b650d67637..ac0b5a7a27 100644 --- a/app/src/main/java/com/osfans/trime/ui/setup/SetupActivity.kt +++ b/app/src/main/java/com/osfans/trime/ui/setup/SetupActivity.kt @@ -47,10 +47,11 @@ class SetupActivity : FragmentActivity() { windowInsets } setContentView(binding.root) - val prevButton = binding.prevButton.apply { - text = getString(R.string.setup__prev) - setOnClickListener { viewPager.currentItem = viewPager.currentItem - 1 } - } + val prevButton = + binding.prevButton.apply { + text = getString(R.string.setup__prev) + setOnClickListener { viewPager.currentItem = viewPager.currentItem - 1 } + } binding.skipButton.apply { text = getString(R.string.setup__skip) setOnClickListener { @@ -63,34 +64,37 @@ class SetupActivity : FragmentActivity() { .show() } } - val nextButton = binding.nextButton.apply { - setOnClickListener { - if (viewPager.currentItem != SetupPage.values().size - 1) { - viewPager.currentItem = viewPager.currentItem + 1 - } else { - finish() + val nextButton = + binding.nextButton.apply { + setOnClickListener { + if (viewPager.currentItem != SetupPage.values().size - 1) { + viewPager.currentItem = viewPager.currentItem + 1 + } else { + finish() + } } } - } viewPager = binding.viewpager viewPager.adapter = Adapter() - viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - // Manually call following observer when page changed - // intentionally before changing the text of nextButton - viewModel.isAllDone.value = viewModel.isAllDone.value - // Hide prev button for the first page - prevButton.visibility = if (position != 0) View.VISIBLE else View.GONE - nextButton.text = - getString( - if (position.isLastPage()) { - R.string.setup__done - } else { - R.string.setup__next - }, - ) - } - }) + viewPager.registerOnPageChangeCallback( + object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + // Manually call following observer when page changed + // intentionally before changing the text of nextButton + viewModel.isAllDone.value = viewModel.isAllDone.value + // Hide prev button for the first page + prevButton.visibility = if (position != 0) View.VISIBLE else View.GONE + nextButton.text = + getString( + if (position.isLastPage()) { + R.string.setup__done + } else { + R.string.setup__next + }, + ) + } + }, + ) viewModel.isAllDone.observe(this) { allDone -> nextButton.apply { // Hide next button for the last page when allDone == false diff --git a/app/src/main/java/com/osfans/trime/ui/setup/SetupPage.kt b/app/src/main/java/com/osfans/trime/ui/setup/SetupPage.kt index 3e20256111..5ecbe7bd31 100644 --- a/app/src/main/java/com/osfans/trime/ui/setup/SetupPage.kt +++ b/app/src/main/java/com/osfans/trime/ui/setup/SetupPage.kt @@ -5,28 +5,33 @@ import com.osfans.trime.R import com.osfans.trime.util.InputMethodUtils enum class SetupPage { - Enable, Select; - - fun getStepText(context: Context) = context.getText( - when (this) { - Enable -> R.string.setup__step_one - Select -> R.string.setup__step_two - }, - ) - - fun getHintText(context: Context) = context.getText( - when (this) { - Enable -> R.string.setup__enable_ime_hint - Select -> R.string.setup__select_ime_hint - }, - ) - - fun getButtonText(context: Context) = context.getText( - when (this) { - Enable -> R.string.setup__enable_ime - Select -> R.string.setup__select_ime - }, - ) + Enable, + Select, + ; + + fun getStepText(context: Context) = + context.getText( + when (this) { + Enable -> R.string.setup__step_one + Select -> R.string.setup__step_two + }, + ) + + fun getHintText(context: Context) = + context.getText( + when (this) { + Enable -> R.string.setup__enable_ime_hint + Select -> R.string.setup__select_ime_hint + }, + ) + + fun getButtonText(context: Context) = + context.getText( + when (this) { + Enable -> R.string.setup__enable_ime + Select -> R.string.setup__select_ime + }, + ) fun getButtonAction(context: Context) { when (this) { @@ -35,15 +40,19 @@ enum class SetupPage { } } - fun isDone() = when (this) { - Enable -> InputMethodUtils.checkIsTrimeEnabled() - Select -> InputMethodUtils.checkIsTrimeSelected() - } + fun isDone() = + when (this) { + Enable -> InputMethodUtils.checkIsTrimeEnabled() + Select -> InputMethodUtils.checkIsTrimeSelected() + } companion object { fun SetupPage.isLastPage() = this == values().last() + fun Int.isLastPage() = this == values().size - 1 + fun hasUndonePage() = values().any { !it.isDone() } + fun firstUndonePage() = values().firstOrNull { !it.isDone() } } } diff --git a/app/src/main/java/com/osfans/trime/util/CollectionUtils.kt b/app/src/main/java/com/osfans/trime/util/CollectionUtils.kt index f69fb9e890..f17f6f309c 100644 --- a/app/src/main/java/com/osfans/trime/util/CollectionUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/CollectionUtils.kt @@ -2,20 +2,28 @@ package com.osfans.trime.util object CollectionUtils { @JvmStatic - fun getOrDefault(map: Map, key: K, defaultValue: V): V = map[key] ?: defaultValue + fun getOrDefault( + map: Map, + key: K, + defaultValue: V, + ): V = map[key] ?: defaultValue @Suppress("UNCHECKED_CAST") @JvmStatic - fun obtainValue(map: Map?, vararg: String?): Any? { + fun obtainValue( + map: Map?, + vararg: String?, + ): Any? { if (map.isNullOrEmpty() || vararg == null) return null val keys = vararg.split('/') var v: Any? = map for (key in keys) { - v = if (v is Map<*, *> && (v as Map).containsKey(key)) { - v[key] - } else { - return null - } + v = + if (v is Map<*, *> && (v as Map).containsKey(key)) { + v[key] + } else { + return null + } } return v } @@ -32,7 +40,11 @@ object CollectionUtils { } @JvmStatic - fun obtainInt(map: Map?, key: String, defValue: Int = 0): Int { + fun obtainInt( + map: Map?, + key: String, + defValue: Int = 0, + ): Int { if (map.isNullOrEmpty() || key.isEmpty()) return defValue val nm = obtainString(map, key) return runCatching { @@ -41,7 +53,11 @@ object CollectionUtils { } @JvmStatic - fun obtainFloat(map: Map?, key: String, defValue: Float = 0f): Float { + fun obtainFloat( + map: Map?, + key: String, + defValue: Float = 0f, + ): Float { if (map.isNullOrEmpty() || key.isEmpty()) return defValue val s = obtainString(map, key) return runCatching { diff --git a/app/src/main/java/com/osfans/trime/util/ColorUtils.kt b/app/src/main/java/com/osfans/trime/util/ColorUtils.kt index b24232fca2..1e9ca68c12 100644 --- a/app/src/main/java/com/osfans/trime/util/ColorUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/ColorUtils.kt @@ -9,24 +9,25 @@ object ColorUtils { if (s == null) return null val hex: String = if (s.startsWith("#")) s.replace("#", "0x") else s return try { - val completed = if (hex.startsWith("0x") || hex.startsWith("0X")) { - when { - hex.length == 3 || hex.length == 4 -> { - String.format("#%02x000000", java.lang.Long.decode(hex)) // 0xA -> #AA000000 - } - hex.length < 8 -> { // 0xGBB -> #RRGGBB - String.format("#%06x", java.lang.Long.decode(hex)) - } - hex.length == 9 -> { // 0xARRGGBB -> #AARRGGBB - "#0" + hex.substring(2) - } - else -> { - "#" + hex.substring(2) // 0xAARRGGBB -> #AARRGGBB, 0xRRGGBB -> #RRGGBB + val completed = + if (hex.startsWith("0x") || hex.startsWith("0X")) { + when { + hex.length == 3 || hex.length == 4 -> { + String.format("#%02x000000", java.lang.Long.decode(hex)) // 0xA -> #AA000000 + } + hex.length < 8 -> { // 0xGBB -> #RRGGBB + String.format("#%06x", java.lang.Long.decode(hex)) + } + hex.length == 9 -> { // 0xARRGGBB -> #AARRGGBB + "#0" + hex.substring(2) + } + else -> { + "#" + hex.substring(2) // 0xAARRGGBB -> #AARRGGBB, 0xRRGGBB -> #RRGGBB + } } + } else { + hex // red, green, blue ... } - } else { - hex // red, green, blue ... - } Color.parseColor(completed) } catch (e: IllegalArgumentException) { Timber.w("Invalid or unknown color value: %s", s) diff --git a/app/src/main/java/com/osfans/trime/util/Const.kt b/app/src/main/java/com/osfans/trime/util/Const.kt index d520efd22e..c247bb09fe 100644 --- a/app/src/main/java/com/osfans/trime/util/Const.kt +++ b/app/src/main/java/com/osfans/trime/util/Const.kt @@ -3,8 +3,8 @@ package com.osfans.trime.util import com.osfans.trime.BuildConfig object Const { - const val buildGitHash = BuildConfig.BUILD_GIT_HASH - const val displayVersionName = "${BuildConfig.BUILD_VERSION_NAME}-${BuildConfig.BUILD_TYPE}" - const val originalGitRepo = "https://github.com/osfans/trime" - const val currentGitRepo = BuildConfig.BUILD_GIT_REPO + val buildGitHash = BuildConfig.BUILD_GIT_HASH + val displayVersionName = "${BuildConfig.BUILD_VERSION_NAME}-${BuildConfig.BUILD_TYPE}" + val originalGitRepo = "https://github.com/osfans/trime" + val currentGitRepo = BuildConfig.BUILD_GIT_REPO } diff --git a/app/src/main/java/com/osfans/trime/util/DialogUtils.kt b/app/src/main/java/com/osfans/trime/util/DialogUtils.kt index 115c6aba5a..94bdb9ca0d 100644 --- a/app/src/main/java/com/osfans/trime/util/DialogUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/DialogUtils.kt @@ -20,7 +20,9 @@ import kotlinx.coroutines.withContext // Adapted from https://github.com/fcitx5-android/fcitx5-android/blob/e37f5513239bab279a9e58cf0c9b163e0dbf5efb/app/src/main/java/org/fcitx/fcitx5/android/ui/common/Preset.kt#L60 @Suppress("FunctionName") -fun Context.ProgressBarDialogIndeterminate(@StringRes titleId: Int): AlertDialog.Builder { +fun Context.ProgressBarDialogIndeterminate( + @StringRes titleId: Int, +): AlertDialog.Builder { return AlertDialog.Builder(this, Theme_AppCompat_DayNight_Dialog_Alert) .setTitle(titleId) .setView( @@ -59,10 +61,11 @@ fun LifecycleCoroutineScope.withLoadingDialog( action: suspend () -> Unit, ) { val loading = context.ProgressBarDialogIndeterminate(titleId).create() - val job = launch { - delay(thresholds) - loading.show() - } + val job = + launch { + delay(thresholds) + loading.show() + } launch { action() job.cancelAndJoin() @@ -77,20 +80,23 @@ suspend fun Context.briefResultLogDialog( priority: String, thresholds: Int, ) = withContext(Dispatchers.Main.immediate) { - val log = withContext(Dispatchers.IO) { - Runtime.getRuntime() - .exec(arrayOf("logcat", "-d", "-v", "brief", "-s", "$tag:$priority")) - .inputStream - .bufferedReader() - .readLines() - } + val log = + withContext(Dispatchers.IO) { + Runtime.getRuntime() + .exec(arrayOf("logcat", "-d", "-v", "brief", "-s", "$tag:$priority")) + .inputStream + .bufferedReader() + .readLines() + } if (log.size > thresholds) { - val logView = LogView(this@briefResultLogDialog).apply { - fromCustomLogLines(log) - layoutParams = MarginLayoutParams(MarginLayoutParams.MATCH_PARENT, MarginLayoutParams.WRAP_CONTENT).apply { - setPadding(dp2px(20), paddingTop, dp2px(20), paddingBottom) + val logView = + LogView(this@briefResultLogDialog).apply { + fromCustomLogLines(log) + layoutParams = + MarginLayoutParams(MarginLayoutParams.MATCH_PARENT, MarginLayoutParams.WRAP_CONTENT).apply { + setPadding(dp2px(20), paddingTop, dp2px(20), paddingBottom) + } } - } AlertDialog.Builder(this@briefResultLogDialog) .setTitle(R.string.setup__done) .setMessage(R.string.found_some_problems) diff --git a/app/src/main/java/com/osfans/trime/util/Drawable.kt b/app/src/main/java/com/osfans/trime/util/Drawable.kt index 8485e1f59e..14791406b0 100644 --- a/app/src/main/java/com/osfans/trime/util/Drawable.kt +++ b/app/src/main/java/com/osfans/trime/util/Drawable.kt @@ -19,20 +19,27 @@ import android.os.Build import androidx.annotation.ColorInt import androidx.annotation.RequiresApi -fun rippleDrawable(@ColorInt color: Int) = - RippleDrawable(ColorStateList.valueOf(color), null, ColorDrawable(Color.WHITE)) +fun rippleDrawable( + @ColorInt color: Int, +) = RippleDrawable(ColorStateList.valueOf(color), null, ColorDrawable(Color.WHITE)) @RequiresApi(Build.VERSION_CODES.M) -fun borderlessRippleDrawable(@ColorInt color: Int, r: Int = RippleDrawable.RADIUS_AUTO) = - RippleDrawable(ColorStateList.valueOf(color), null, null).apply { - radius = r - } +fun borderlessRippleDrawable( + @ColorInt color: Int, + r: Int = RippleDrawable.RADIUS_AUTO, +) = RippleDrawable(ColorStateList.valueOf(color), null, null).apply { + radius = r +} -fun pressHighlightDrawable(@ColorInt color: Int) = StateListDrawable().apply { +fun pressHighlightDrawable( + @ColorInt color: Int, +) = StateListDrawable().apply { addState(intArrayOf(android.R.attr.state_pressed), ColorDrawable(color)) } -fun circlePressHighlightDrawable(@ColorInt color: Int) = StateListDrawable().apply { +fun circlePressHighlightDrawable( + @ColorInt color: Int, +) = StateListDrawable().apply { addState( intArrayOf(android.R.attr.state_pressed), ShapeDrawable(OvalShape()).apply { paint.color = color }, diff --git a/app/src/main/java/com/osfans/trime/util/Exception.kt b/app/src/main/java/com/osfans/trime/util/Exception.kt index 19fdf1b9b6..3e5234bdc2 100644 --- a/app/src/main/java/com/osfans/trime/util/Exception.kt +++ b/app/src/main/java/com/osfans/trime/util/Exception.kt @@ -18,11 +18,17 @@ inline fun errorT( ), ) -fun errorState(@StringRes messageTemplate: Int, messageArg: String? = null): Nothing = - errorT(::IllegalStateException, messageTemplate, messageArg) +fun errorState( + @StringRes messageTemplate: Int, + messageArg: String? = null, +): Nothing = errorT(::IllegalStateException, messageTemplate, messageArg) -fun errorArg(@StringRes messageTemplate: Int, messageArg: String? = null): Nothing = - errorT(::IllegalArgumentException, messageTemplate, messageArg) +fun errorArg( + @StringRes messageTemplate: Int, + messageArg: String? = null, +): Nothing = errorT(::IllegalArgumentException, messageTemplate, messageArg) -fun errorRuntime(@StringRes messageTemplate: Int, messageArg: String? = null): Nothing = - errorT(::RuntimeException, messageTemplate, messageArg) +fun errorRuntime( + @StringRes messageTemplate: Int, + messageArg: String? = null, +): Nothing = errorT(::RuntimeException, messageTemplate, messageArg) diff --git a/app/src/main/java/com/osfans/trime/util/GraphicUtils.kt b/app/src/main/java/com/osfans/trime/util/GraphicUtils.kt index 85ed688461..71db9d0a26 100644 --- a/app/src/main/java/com/osfans/trime/util/GraphicUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/GraphicUtils.kt @@ -13,7 +13,10 @@ object GraphicUtils { private val hanBFont = FontManager.getTypeface(theme.style.getString(HAN_B_FONT)) private val latinFont = FontManager.getTypeface(theme.style.getString(LATIN_FONT)) - private fun determineTypeface(codePoint: Int, font: Typeface): Typeface { + private fun determineTypeface( + codePoint: Int, + font: Typeface, + ): Typeface { return if (hanBFont != Typeface.DEFAULT && Character.isSupplementaryCodePoint(codePoint)) { hanBFont } else if (latinFont != Typeface.DEFAULT && codePoint < 0x2E80) { @@ -24,7 +27,10 @@ object GraphicUtils { } @JvmStatic - fun Paint.measureText(text: String, font: Typeface): Float { + fun Paint.measureText( + text: String, + font: Typeface, + ): Float { if (text.isEmpty()) return 0.0f val codePoints = text.codePointCount(0, text.length) var x = 0.0f @@ -48,7 +54,13 @@ object GraphicUtils { } @JvmStatic - fun Canvas.drawText(text: String, centerX: Float, y: Float, paint: Paint, font: Typeface) { + fun Canvas.drawText( + text: String, + centerX: Float, + y: Float, + paint: Paint, + font: Typeface, + ) { if (text.isEmpty()) return val codePoints = text.codePointCount(0, text.length) var x = centerX - paint.measureText(text, font) / 2 diff --git a/app/src/main/java/com/osfans/trime/util/InputMethodUtils.kt b/app/src/main/java/com/osfans/trime/util/InputMethodUtils.kt index a8d2e31bfb..6804be7f15 100644 --- a/app/src/main/java/com/osfans/trime/util/InputMethodUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/InputMethodUtils.kt @@ -12,8 +12,7 @@ object InputMethodUtils { private val serviceName = ComponentName(appContext, TrimeImeService::class.java).flattenToShortString() - private fun getSecureSettings(name: String) = - Settings.Secure.getString(appContext.contentResolver, name) + private fun getSecureSettings(name: String) = Settings.Secure.getString(appContext.contentResolver, name) fun checkIsTrimeEnabled(): Boolean { val activeImeIds = getSecureSettings(Settings.Secure.ENABLED_INPUT_METHODS) ?: "(none)" @@ -27,8 +26,7 @@ object InputMethodUtils { return selectedImeIds == serviceName } - fun showImeEnablerActivity(context: Context) = - context.startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS)) + fun showImeEnablerActivity(context: Context) = context.startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS)) fun showImePicker(): Boolean { inputMethodManager.showInputMethodPicker() diff --git a/app/src/main/java/com/osfans/trime/util/Logcat.kt b/app/src/main/java/com/osfans/trime/util/Logcat.kt index 4fee82403a..e84a6d2bbc 100644 --- a/app/src/main/java/com/osfans/trime/util/Logcat.kt +++ b/app/src/main/java/com/osfans/trime/util/Logcat.kt @@ -19,7 +19,6 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch class Logcat(val pid: Int? = Process.myPid()) : CoroutineScope by CoroutineScope(Dispatchers.IO) { - private var process: java.lang.Process? = null private var emittingJob: Job? = null @@ -34,15 +33,16 @@ class Logcat(val pid: Int? = Process.myPid()) : CoroutineScope by CoroutineScope /** * Get a snapshot of logcat */ - fun getLogAsync(): Deferred>> = async { - runCatching { - Runtime.getRuntime() - .exec(arrayOf("logcat", pid?.let { "--pid=$it" } ?: "", "-d")) - .inputStream - .bufferedReader() - .readLines() + fun getLogAsync(): Deferred>> = + async { + runCatching { + Runtime.getRuntime() + .exec(arrayOf("logcat", pid?.let { "--pid=$it" } ?: "", "-d")) + .inputStream + .bufferedReader() + .readLines() + } } - } /** * Clear logcat diff --git a/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt b/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt index f766d29e1d..fc04d3dcb8 100644 --- a/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt @@ -30,7 +30,11 @@ import java.util.Locale * Implementation to open/call specified application/function */ object ShortcutUtils { - fun call(context: Context, command: String, option: String): CharSequence? { + fun call( + context: Context, + command: String, + option: String, + ): CharSequence? { when (command) { "broadcast" -> context.sendBroadcast(Intent(option)) "clipboard" -> return pasteFromClipboard(context) @@ -45,26 +49,30 @@ object ShortcutUtils { } private fun startIntent(arg: String) { - val intent = when { - arg.indexOf(':') >= 0 -> { - Intent.parseUri(arg, Intent.URI_INTENT_SCHEME) - } - arg.indexOf('/') >= 0 -> { - Intent(Intent.ACTION_MAIN).apply { - addCategory(Intent.CATEGORY_LAUNCHER) - component = ComponentName.unflattenFromString(arg) + val intent = + when { + arg.indexOf(':') >= 0 -> { + Intent.parseUri(arg, Intent.URI_INTENT_SCHEME) + } + arg.indexOf('/') >= 0 -> { + Intent(Intent.ACTION_MAIN).apply { + addCategory(Intent.CATEGORY_LAUNCHER) + component = ComponentName.unflattenFromString(arg) + } } + else -> IntentUtils.getLaunchAppIntent(arg) } - else -> IntentUtils.getLaunchAppIntent(arg) - } intent.flags = ( Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY - ) + ) ActivityUtils.startActivity(intent) } - private fun startIntent(action: String, arg: String) { + private fun startIntent( + action: String, + arg: String, + ) { val act = "android.intent.action.${action.uppercase()}" var intent = Intent(act) when (act) { @@ -105,11 +113,12 @@ object ShortcutUtils { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !TextUtils.isEmpty(locale)) { val ul = ULocale(locale) val cc = Calendar.getInstance(ul) - val df = if (option.isEmpty()) { - DateFormat.getDateInstance(DateFormat.LONG, ul) - } else { - android.icu.text.SimpleDateFormat(option, ul.toLocale()) - } + val df = + if (option.isEmpty()) { + DateFormat.getDateInstance(DateFormat.LONG, ul) + } else { + android.icu.text.SimpleDateFormat(option, ul.toLocale()) + } df.format(cc, StringBuffer(256), FieldPosition(0)).toString() } else { SimpleDateFormat(string, Locale.getDefault()).format(Date()) // Time @@ -140,14 +149,15 @@ object ShortcutUtils { } } - private val applicationLaunchKeyCategories = SparseArray().apply { - append(KeyEvent.KEYCODE_EXPLORER, "android.intent.category.APP_BROWSER") - append(KeyEvent.KEYCODE_ENVELOPE, "android.intent.category.APP_EMAIL") - append(KeyEvent.KEYCODE_CONTACTS, "android.intent.category.APP_CONTACTS") - append(KeyEvent.KEYCODE_CALENDAR, "android.intent.category.APP_CALENDAR") - append(KeyEvent.KEYCODE_MUSIC, "android.intent.category.APP_MUSIC") - append(KeyEvent.KEYCODE_CALCULATOR, "android.intent.category.APP_CALCULATOR") - } + private val applicationLaunchKeyCategories = + SparseArray().apply { + append(KeyEvent.KEYCODE_EXPLORER, "android.intent.category.APP_BROWSER") + append(KeyEvent.KEYCODE_ENVELOPE, "android.intent.category.APP_EMAIL") + append(KeyEvent.KEYCODE_CONTACTS, "android.intent.category.APP_CONTACTS") + append(KeyEvent.KEYCODE_CALENDAR, "android.intent.category.APP_CALENDAR") + append(KeyEvent.KEYCODE_MUSIC, "android.intent.category.APP_MUSIC") + append(KeyEvent.KEYCODE_CALCULATOR, "android.intent.category.APP_CALCULATOR") + } fun launchMainActivity(context: Context) { context.startActivity( diff --git a/app/src/main/java/com/osfans/trime/util/StringUtils.kt b/app/src/main/java/com/osfans/trime/util/StringUtils.kt index c911fc4a06..1ebc30bc50 100644 --- a/app/src/main/java/com/osfans/trime/util/StringUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/StringUtils.kt @@ -4,7 +4,10 @@ object StringUtils { private const val SECTION_DIVIDER = ",.?!~:,。:~?!…\t\r\n\\/" @JvmStatic - fun findSectionAfter(cs: CharSequence?, startIndex: Int): Int { + fun findSectionAfter( + cs: CharSequence?, + startIndex: Int, + ): Int { cs ?: return 0 val index = startIndex.coerceAtLeast(0) for ((i, c) in cs.withIndex()) { @@ -15,7 +18,10 @@ object StringUtils { } @JvmStatic - fun findSectionBefore(cs: CharSequence?, startIndex: Int): Int { + fun findSectionBefore( + cs: CharSequence?, + startIndex: Int, + ): Int { cs ?: return 0 val index = startIndex.coerceAtMost(cs.length) - 1 for ((i, c) in cs.withIndex().reversed()) { diff --git a/app/src/main/java/com/osfans/trime/util/Utils.kt b/app/src/main/java/com/osfans/trime/util/Utils.kt index e38abcad88..551030fbb9 100644 --- a/app/src/main/java/com/osfans/trime/util/Utils.kt +++ b/app/src/main/java/com/osfans/trime/util/Utils.kt @@ -39,17 +39,17 @@ inline fun Result.bindOnNotNull(block: (T) -> Result): Resul } } -suspend fun Result.toast() = withContext(Dispatchers.Main.immediate) { - onSuccess { - ToastUtils.showShort(R.string.setup__done) - } - onFailure { - ToastUtils.showShort(it.message) +suspend fun Result.toast() = + withContext(Dispatchers.Main.immediate) { + onSuccess { + ToastUtils.showShort(R.string.setup__done) + } + onFailure { + ToastUtils.showShort(it.message) + } } -} -fun formatDateTime(timeMillis: Long? = null): String = - SimpleDateFormat.getDateTimeInstance().format(timeMillis?.let { Date(it) } ?: Date()) +fun formatDateTime(timeMillis: Long? = null): String = SimpleDateFormat.getDateTimeInstance().format(timeMillis?.let { Date(it) } ?: Date()) private val iso8601DateFormat by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).apply { @@ -57,8 +57,7 @@ private val iso8601DateFormat by lazy { } } -fun iso8601UTCDateTime(timeMillis: Long? = null): String = - iso8601DateFormat.format(timeMillis?.let { Date(it) } ?: Date()) +fun iso8601UTCDateTime(timeMillis: Long? = null): String = iso8601DateFormat.format(timeMillis?.let { Date(it) } ?: Date()) @Suppress("NOTHING_TO_INLINE") inline fun CharSequence.startsWithAsciiChar(): Boolean { @@ -100,11 +99,12 @@ inline fun Bundle.serializable(key: String): T? { fun Preference.thirdPartySummary(versionCode: String) { summary = versionCode intent?.let { - val commitHash = if (versionCode.contains("-g")) { - versionCode.replace("^(.*-g)([0-9a-f]+)(.*)$".toRegex(), "$2") - } else { - versionCode.replace("^([^-]*)(-.*)$".toRegex(), "$1") - } + val commitHash = + if (versionCode.contains("-g")) { + versionCode.replace("^(.*-g)([0-9a-f]+)(.*)$".toRegex(), "$2") + } else { + versionCode.replace("^([^-]*)(-.*)$".toRegex(), "$1") + } it.data = Uri.withAppendedPath(it.data, "commits/$commitHash") } } diff --git a/app/src/main/java/com/osfans/trime/util/ViewUtils.kt b/app/src/main/java/com/osfans/trime/util/ViewUtils.kt index 6329dbe83d..9c70346319 100644 --- a/app/src/main/java/com/osfans/trime/util/ViewUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/ViewUtils.kt @@ -21,7 +21,10 @@ import android.widget.LinearLayout */ object ViewUtils { @JvmStatic - fun updateLayoutHeightOf(window: Window, layoutHeight: Int) { + fun updateLayoutHeightOf( + window: Window, + layoutHeight: Int, + ) { val params = window.attributes if (params != null && params.height != layoutHeight) { params.height = layoutHeight @@ -30,7 +33,10 @@ object ViewUtils { } @JvmStatic - fun updateLayoutHeightOf(view: View, layoutHeight: Int) { + fun updateLayoutHeightOf( + view: View, + layoutHeight: Int, + ) { val params = view.layoutParams if (params != null && params.height != layoutHeight) { params.height = layoutHeight @@ -39,7 +45,10 @@ object ViewUtils { } @JvmStatic - fun updateLayoutGravityOf(view: View, layoutGravity: Int) { + fun updateLayoutGravityOf( + view: View, + layoutGravity: Int, + ) { val lp = view.layoutParams if (lp is LinearLayout.LayoutParams) { if (lp.gravity != layoutGravity) { diff --git a/app/src/main/java/com/osfans/trime/util/WeakHashSet.kt b/app/src/main/java/com/osfans/trime/util/WeakHashSet.kt index c1ddf5f414..541014803a 100644 --- a/app/src/main/java/com/osfans/trime/util/WeakHashSet.kt +++ b/app/src/main/java/com/osfans/trime/util/WeakHashSet.kt @@ -3,7 +3,6 @@ package com.osfans.trime.util import java.util.WeakHashMap class WeakHashSet : MutableSet { - private val core = WeakHashMap() private object PlaceHolder @@ -14,16 +13,19 @@ class WeakHashSet : MutableSet { override fun iterator(): MutableIterator = core.keys.iterator() override fun add(element: T) = core.put(element, PlaceHolder) != null + override fun addAll(elements: Collection) = elements.all(::add) override fun remove(element: T) = core.remove(element) != null + override fun removeAll(elements: Collection) = elements.all(::remove) + override fun clear() = core.clear() - override fun retainAll(elements: Collection) = - removeAll(core.keys.filter { it !in elements }) + override fun retainAll(elements: Collection) = removeAll(core.keys.filter { it !in elements }) override operator fun contains(element: T) = core.containsKey(element) + override fun containsAll(elements: Collection) = elements.all(::contains) override fun isEmpty() = core.isEmpty() diff --git a/app/src/main/java/com/osfans/trime/util/config/Config.kt b/app/src/main/java/com/osfans/trime/util/config/Config.kt index 81b13b5feb..f998bba523 100644 --- a/app/src/main/java/com/osfans/trime/util/config/Config.kt +++ b/app/src/main/java/com/osfans/trime/util/config/Config.kt @@ -7,7 +7,6 @@ import timber.log.Timber * New YAML config parser intended to replace the old one. */ class Config(private val data: ConfigData = ConfigData()) { - companion object { fun create(fileName: String): Config? { val data = ConfigData() @@ -41,25 +40,37 @@ class Config(private val data: ConfigData = ConfigData()) { return p == null || p.type == ConfigItem.ValueType.Map } - fun getBool(path: String, defValue: Boolean = false): Boolean { + fun getBool( + path: String, + defValue: Boolean = false, + ): Boolean { Timber.d("read: $path") val p = data.traverse(path)?.configValue return p?.getBool() ?: defValue } - fun getInt(path: String, defValue: Int = 0): Int { + fun getInt( + path: String, + defValue: Int = 0, + ): Int { Timber.d("read: $path") val p = data.traverse(path)?.configValue return p?.getInt() ?: defValue } - fun getFloat(path: String, defValue: Float = 0f): Float { + fun getFloat( + path: String, + defValue: Float = 0f, + ): Float { Timber.d("read: $path") val p = data.traverse(path)?.configValue return p?.getFloat() ?: defValue } - fun getString(path: String, defValue: String = ""): String { + fun getString( + path: String, + defValue: String = "", + ): String { Timber.d("read: $path") val p = data.traverse(path)?.configValue return p?.getString() ?: defValue diff --git a/app/src/main/java/com/osfans/trime/util/config/ConfigData.kt b/app/src/main/java/com/osfans/trime/util/config/ConfigData.kt index 925eaf150c..206a04004c 100644 --- a/app/src/main/java/com/osfans/trime/util/config/ConfigData.kt +++ b/app/src/main/java/com/osfans/trime/util/config/ConfigData.kt @@ -30,11 +30,13 @@ fun convertFromYaml(node: YamlNode): ConfigItem? { class ConfigData { var root: ConfigItem? = null - private val yaml = Yaml( - configuration = YamlConfiguration( - strictMode = false, - ), - ) + private val yaml = + Yaml( + configuration = + YamlConfiguration( + strictMode = false, + ), + ) fun loadFromFile(fileName: String): Boolean { val configFile = File(fileName) @@ -44,10 +46,11 @@ class ConfigData { } Timber.i("Loading config file $fileName") try { - val doc = configFile - .inputStream() - .bufferedReader() - .use { it.readText() } + val doc = + configFile + .inputStream() + .bufferedReader() + .use { it.readText() } val node = yaml.parseToYamlNode(doc) root = convertFromYaml(node) } catch (e: YamlException) { diff --git a/app/src/main/java/com/osfans/trime/util/config/ConfigTypes.kt b/app/src/main/java/com/osfans/trime/util/config/ConfigTypes.kt index ab91d4f0db..71ec41b080 100644 --- a/app/src/main/java/com/osfans/trime/util/config/ConfigTypes.kt +++ b/app/src/main/java/com/osfans/trime/util/config/ConfigTypes.kt @@ -15,16 +15,23 @@ import kotlinx.serialization.DeserializationStrategy /** Config item base class */ abstract class ConfigItem(val node: YamlNode) { enum class ValueType { - Null, Scalar, List, Map, Tagged + Null, + Scalar, + List, + Map, + Tagged, } + open fun isEmpty() = node is YamlNull - val type get() = when (node) { - is YamlNull -> ValueType.Null - is YamlScalar -> ValueType.Scalar - is YamlList -> ValueType.List - is YamlMap -> ValueType.Map - else -> ValueType.Null - } + + val type get() = + when (node) { + is YamlNull -> ValueType.Null + is YamlScalar -> ValueType.Scalar + is YamlList -> ValueType.List + is YamlMap -> ValueType.Map + else -> ValueType.Null + } abstract fun contentToString(): String @@ -41,7 +48,10 @@ abstract class ConfigItem(val node: YamlNode) { val configMap: ConfigMap get() = this as? ConfigMap ?: error(this, "ConfigMap") - private fun error(item: ConfigItem, expectedType: String): Nothing { + private fun error( + item: ConfigItem, + expectedType: String, + ): Nothing { throw IllegalArgumentException("Expected element to be $expectedType bus is ${item::class.simpleName}") } } @@ -51,11 +61,15 @@ class ConfigValue(private val scalar: YamlScalar) : ConfigItem(scalar) { constructor(item: ConfigItem) : this(item.node.yamlScalar) fun getString() = scalar.content + fun getInt() = scalar.toInt() + fun getFloat() = scalar.toFloat() + fun getBool() = scalar.toBoolean() override fun isEmpty() = scalar.content.isEmpty() + override fun contentToString(): String = scalar.contentToString() } @@ -68,9 +82,11 @@ class ConfigList(private val list: YamlList) : ConfigItem(list) { operator fun iterator() = items.iterator() override fun isEmpty() = list.items.isEmpty() + override fun contentToString(): String = list.contentToString() operator fun get(index: Int) = items[index] + fun getValue(index: Int) = get(index)?.configValue } @@ -78,19 +94,20 @@ class ConfigMap(private val map: YamlMap) : ConfigItem(map) { constructor(item: ConfigItem) : this(item.node.yamlMap) override fun isEmpty() = map.entries.isEmpty() + override fun contentToString(): String = map.contentToString() fun containsKey(key: String) = map.getKey(key) != null - val entries get() = map.entries.entries.associate { (s, n) -> - s.content to convertFromYaml(n) - } + val entries get() = + map.entries.entries.associate { (s, n) -> + s.content to convertFromYaml(n) + } operator fun iterator() = entries.iterator() @Suppress("UNCHECKED_CAST") - operator fun get(key: String): T? = - entries.entries.firstOrNull { it.key == key }?.value as T? + operator fun get(key: String): T? = entries.entries.firstOrNull { it.key == key }?.value as T? fun getValue(key: String): ConfigValue? = get(key)?.configValue } diff --git a/codegen/src/main/java/com/osfans/trime/codegen/GenKeyMapping.kt b/codegen/src/main/java/com/osfans/trime/codegen/GenKeyMapping.kt index f2fb33e559..858a2985c5 100644 --- a/codegen/src/main/java/com/osfans/trime/codegen/GenKeyMapping.kt +++ b/codegen/src/main/java/com/osfans/trime/codegen/GenKeyMapping.kt @@ -31,171 +31,166 @@ private typealias KeyPair = Pair, AndroidKeyCode> * The original source code can be found at the following location: * https://github.com/fcitx5-android/fcitx5-android/blob/14fe8c589ecb1546ed76445df0de658f81c4a1ed/codegen/src/main/java/org/fcitx/fcitx5/android/codegen/GenKeyMapping.kt */ +@Suppress("ktlint:standard:discouraged-comment-location") internal class GenKeyMappingProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor { - - @Suppress("ktlint:standard:comment-wrapping") - private val pairs: List = listOf( - "space" to 0x0020 to "KEYCODE_SPACE", /* U+0020 SPACE */ + @Suppress("ktlint:standard:no-consecutive-comments") + private val pairs: List = + listOf( + "space" to 0x0020 to "KEYCODE_SPACE", // U+0020 SPACE // 0x0021 to KeyEvent.KEYCODE_EXCLAM, /* U+0021 EXCLAMATION MARK */ // 0x0022 to KeyEvent.KEYCODE_QUOTEDBL, /* U+0022 QUOTATION MARK */ - "numbersign" to 0x0023 to "KEYCODE_POUND", /* U+0023 NUMBER SIGN */ + "numbersign" to 0x0023 to "KEYCODE_POUND", // U+0023 NUMBER SIGN // 0x0024 to KeyEvent.KEYCODE_DOLLAR, /* U+0024 DOLLAR SIGN */ // 0x0025 to KeyEvent.KEYCODE_PERCENT, /* U+0025 PERCENT SIGN */ // 0x0026 to KeyEvent.KEYCODE_AMPERSAND, /* U+0026 AMPERSAND */ - "apostrophe" to 0x0027 to "KEYCODE_APOSTROPHE", /* U+0027 APOSTROPHE */ + "apostrophe" to 0x0027 to "KEYCODE_APOSTROPHE", // U+0027 APOSTROPHE // 0x0027 to KeyEvent.KEYCODE_QUOTERIGHT, /* deprecated */ // 0x0028 to KeyEvent.KEYCODE_PARENLEFT, /* U+0028 LEFT PARENTHESIS */ // 0x0029 to KeyEvent.KEYCODE_PARENRIGHT, /* U+0029 RIGHT PARENTHESIS */ - "asterisk" to 0x002a to "KEYCODE_STAR", /* U+002A ASTERISK */ - "plus" to 0x002b to "KEYCODE_PLUS", /* U+002B PLUS SIGN */ - "comma" to 0x002c to "KEYCODE_COMMA", /* U+002C COMMA */ - "minus" to 0x002d to "KEYCODE_MINUS", /* U+002D HYPHEN-MINUS */ - "period" to 0x002e to "KEYCODE_PERIOD", /* U+002E FULL STOP */ - "slash" to 0x002f to "KEYCODE_SLASH", /* U+002F SOLIDUS */ - "0" to 0x0030 to "KEYCODE_0", /* U+0030 DIGIT ZERO */ - "1" to 0x0031 to "KEYCODE_1", /* U+0031 DIGIT ONE */ - "2" to 0x0032 to "KEYCODE_2", /* U+0032 DIGIT TWO */ - "3" to 0x0033 to "KEYCODE_3", /* U+0033 DIGIT THREE */ - "4" to 0x0034 to "KEYCODE_4", /* U+0034 DIGIT FOUR */ - "5" to 0x0035 to "KEYCODE_5", /* U+0035 DIGIT FIVE */ - "6" to 0x0036 to "KEYCODE_6", /* U+0036 DIGIT SIX */ - "7" to 0x0037 to "KEYCODE_7", /* U+0037 DIGIT SEVEN */ - "8" to 0x0038 to "KEYCODE_8", /* U+0038 DIGIT EIGHT */ - "9" to 0x0039 to "KEYCODE_9", /* U+0039 DIGIT NINE */ + "asterisk" to 0x002a to "KEYCODE_STAR", // U+002A ASTERISK + "plus" to 0x002b to "KEYCODE_PLUS", // U+002B PLUS SIGN + "comma" to 0x002c to "KEYCODE_COMMA", // U+002C COMMA + "minus" to 0x002d to "KEYCODE_MINUS", // U+002D HYPHEN-MINUS + "period" to 0x002e to "KEYCODE_PERIOD", // U+002E FULL STOP + "slash" to 0x002f to "KEYCODE_SLASH", // U+002F SOLIDUS + "0" to 0x0030 to "KEYCODE_0", // U+0030 DIGIT ZERO + "1" to 0x0031 to "KEYCODE_1", // U+0031 DIGIT ONE + "2" to 0x0032 to "KEYCODE_2", // U+0032 DIGIT TWO + "3" to 0x0033 to "KEYCODE_3", // U+0033 DIGIT THREE + "4" to 0x0034 to "KEYCODE_4", // U+0034 DIGIT FOUR + "5" to 0x0035 to "KEYCODE_5", // U+0035 DIGIT FIVE + "6" to 0x0036 to "KEYCODE_6", // U+0036 DIGIT SIX + "7" to 0x0037 to "KEYCODE_7", // U+0037 DIGIT SEVEN + "8" to 0x0038 to "KEYCODE_8", // U+0038 DIGIT EIGHT + "9" to 0x0039 to "KEYCODE_9", // U+0039 DIGIT NINE // 0x003a to KeyEvent.KEYCODE_COLON, /* U+003A COLON */ - "semicolon" to 0x003b to "KEYCODE_SEMICOLON", /* U+003B SEMICOLON */ + "semicolon" to 0x003b to "KEYCODE_SEMICOLON", // U+003B SEMICOLON // 0x003c to KeyEvent.KEYCODE_LESS, /* U+003C LESS-THAN SIGN */ - "equal" to 0x003d to "KEYCODE_EQUALS", /* U+003D EQUALS SIGN */ + "equal" to 0x003d to "KEYCODE_EQUALS", // U+003D EQUALS SIGN // 0x003e to KeyEvent.KEYCODE_GREATER, /* U+003E GREATER-THAN SIGN */ // 0x003f to KeyEvent.KEYCODE_QUESTION, /* U+003F QUESTION MARK */ - "at" to 0x0040 to "KEYCODE_AT", /* U+0040 COMMERCIAL AT */ - "A" to 0x0041 to "KEYCODE_A", /* U+0041 LATIN CAPITAL LETTER A */ - "B" to 0x0042 to "KEYCODE_B", /* U+0042 LATIN CAPITAL LETTER B */ - "C" to 0x0043 to "KEYCODE_C", /* U+0043 LATIN CAPITAL LETTER C */ - "D" to 0x0044 to "KEYCODE_D", /* U+0044 LATIN CAPITAL LETTER D */ - "E" to 0x0045 to "KEYCODE_E", /* U+0045 LATIN CAPITAL LETTER E */ - "F" to 0x0046 to "KEYCODE_F", /* U+0046 LATIN CAPITAL LETTER F */ - "G" to 0x0047 to "KEYCODE_G", /* U+0047 LATIN CAPITAL LETTER G */ - "H" to 0x0048 to "KEYCODE_H", /* U+0048 LATIN CAPITAL LETTER H */ - "I" to 0x0049 to "KEYCODE_I", /* U+0049 LATIN CAPITAL LETTER I */ - "J" to 0x004a to "KEYCODE_J", /* U+004A LATIN CAPITAL LETTER J */ - "K" to 0x004b to "KEYCODE_K", /* U+004B LATIN CAPITAL LETTER K */ - "L" to 0x004c to "KEYCODE_L", /* U+004C LATIN CAPITAL LETTER L */ - "M" to 0x004d to "KEYCODE_M", /* U+004D LATIN CAPITAL LETTER M */ - "N" to 0x004e to "KEYCODE_N", /* U+004E LATIN CAPITAL LETTER N */ - "O" to 0x004f to "KEYCODE_O", /* U+004F LATIN CAPITAL LETTER O */ - "P" to 0x0050 to "KEYCODE_P", /* U+0050 LATIN CAPITAL LETTER P */ - "Q" to 0x0051 to "KEYCODE_Q", /* U+0051 LATIN CAPITAL LETTER Q */ - "R" to 0x0052 to "KEYCODE_R", /* U+0052 LATIN CAPITAL LETTER R */ - "S" to 0x0053 to "KEYCODE_S", /* U+0053 LATIN CAPITAL LETTER S */ - "T" to 0x0054 to "KEYCODE_T", /* U+0054 LATIN CAPITAL LETTER T */ - "U" to 0x0055 to "KEYCODE_U", /* U+0055 LATIN CAPITAL LETTER U */ - "V" to 0x0056 to "KEYCODE_V", /* U+0056 LATIN CAPITAL LETTER V */ - "W" to 0x0057 to "KEYCODE_W", /* U+0057 LATIN CAPITAL LETTER W */ - "X" to 0x0058 to "KEYCODE_X", /* U+0058 LATIN CAPITAL LETTER X */ - "Y" to 0x0059 to "KEYCODE_Y", /* U+0059 LATIN CAPITAL LETTER Y */ - "Z" to 0x005a to "KEYCODE_Z", /* U+005A LATIN CAPITAL LETTER Z */ - "bracketleft" to 0x005b to "KEYCODE_LEFT_BRACKET", /* U+005B LEFT SQUARE BRACKET */ - "backslash" to 0x005c to "KEYCODE_BACKSLASH", /* U+005C REVERSE SOLIDUS */ - "bracketright" to 0x005d to "KEYCODE_RIGHT_BRACKET", /* U+005D RIGHT SQUARE BRACKET */ + "at" to 0x0040 to "KEYCODE_AT", // U+0040 COMMERCIAL AT + "A" to 0x0041 to "KEYCODE_A", // U+0041 LATIN CAPITAL LETTER A + "B" to 0x0042 to "KEYCODE_B", // U+0042 LATIN CAPITAL LETTER B + "C" to 0x0043 to "KEYCODE_C", // U+0043 LATIN CAPITAL LETTER C + "D" to 0x0044 to "KEYCODE_D", // U+0044 LATIN CAPITAL LETTER D + "E" to 0x0045 to "KEYCODE_E", // U+0045 LATIN CAPITAL LETTER E + "F" to 0x0046 to "KEYCODE_F", // U+0046 LATIN CAPITAL LETTER F + "G" to 0x0047 to "KEYCODE_G", // U+0047 LATIN CAPITAL LETTER G + "H" to 0x0048 to "KEYCODE_H", // U+0048 LATIN CAPITAL LETTER H + "I" to 0x0049 to "KEYCODE_I", // U+0049 LATIN CAPITAL LETTER I + "J" to 0x004a to "KEYCODE_J", // U+004A LATIN CAPITAL LETTER J + "K" to 0x004b to "KEYCODE_K", // U+004B LATIN CAPITAL LETTER K + "L" to 0x004c to "KEYCODE_L", // U+004C LATIN CAPITAL LETTER L + "M" to 0x004d to "KEYCODE_M", // U+004D LATIN CAPITAL LETTER M + "N" to 0x004e to "KEYCODE_N", // U+004E LATIN CAPITAL LETTER N + "O" to 0x004f to "KEYCODE_O", // U+004F LATIN CAPITAL LETTER O + "P" to 0x0050 to "KEYCODE_P", // U+0050 LATIN CAPITAL LETTER P + "Q" to 0x0051 to "KEYCODE_Q", // U+0051 LATIN CAPITAL LETTER Q + "R" to 0x0052 to "KEYCODE_R", // U+0052 LATIN CAPITAL LETTER R + "S" to 0x0053 to "KEYCODE_S", // U+0053 LATIN CAPITAL LETTER S + "T" to 0x0054 to "KEYCODE_T", // U+0054 LATIN CAPITAL LETTER T + "U" to 0x0055 to "KEYCODE_U", // U+0055 LATIN CAPITAL LETTER U + "V" to 0x0056 to "KEYCODE_V", // U+0056 LATIN CAPITAL LETTER V + "W" to 0x0057 to "KEYCODE_W", // U+0057 LATIN CAPITAL LETTER W + "X" to 0x0058 to "KEYCODE_X", // U+0058 LATIN CAPITAL LETTER X + "Y" to 0x0059 to "KEYCODE_Y", // U+0059 LATIN CAPITAL LETTER Y + "Z" to 0x005a to "KEYCODE_Z", // U+005A LATIN CAPITAL LETTER Z + "bracketleft" to 0x005b to "KEYCODE_LEFT_BRACKET", // U+005B LEFT SQUARE BRACKET + "backslash" to 0x005c to "KEYCODE_BACKSLASH", // U+005C REVERSE SOLIDUS + "bracketright" to 0x005d to "KEYCODE_RIGHT_BRACKET", // U+005D RIGHT SQUARE BRACKET // 0x005e to KeyEvent.KEYCODE_ASCIICIRCUM, /* U+005E CIRCUMFLEX ACCENT */ // 0x005f to KeyEvent.KEYCODE_UNDERSCORE, /* U+005F LOW LINE */ - "grave" to 0x0060 to "KEYCODE_GRAVE", /* U+0060 GRAVE ACCENT */ + "grave" to 0x0060 to "KEYCODE_GRAVE", // U+0060 GRAVE ACCENT // 0x0060 to KeyEvent.KEYCODE_QUOTELEFT, /* deprecated */ - "a" to 0x0061 to "KEYCODE_A", /* U+0061 LATIN SMALL LETTER A */ - "b" to 0x0062 to "KEYCODE_B", /* U+0062 LATIN SMALL LETTER B */ - "c" to 0x0063 to "KEYCODE_C", /* U+0063 LATIN SMALL LETTER C */ - "d" to 0x0064 to "KEYCODE_D", /* U+0064 LATIN SMALL LETTER D */ - "e" to 0x0065 to "KEYCODE_E", /* U+0065 LATIN SMALL LETTER E */ - "f" to 0x0066 to "KEYCODE_F", /* U+0066 LATIN SMALL LETTER F */ - "g" to 0x0067 to "KEYCODE_G", /* U+0067 LATIN SMALL LETTER G */ - "h" to 0x0068 to "KEYCODE_H", /* U+0068 LATIN SMALL LETTER H */ - "i" to 0x0069 to "KEYCODE_I", /* U+0069 LATIN SMALL LETTER I */ - "j" to 0x006a to "KEYCODE_J", /* U+006A LATIN SMALL LETTER J */ - "k" to 0x006b to "KEYCODE_K", /* U+006B LATIN SMALL LETTER K */ - "l" to 0x006c to "KEYCODE_L", /* U+006C LATIN SMALL LETTER L */ - "m" to 0x006d to "KEYCODE_M", /* U+006D LATIN SMALL LETTER M */ - "n" to 0x006e to "KEYCODE_N", /* U+006E LATIN SMALL LETTER N */ - "o" to 0x006f to "KEYCODE_O", /* U+006F LATIN SMALL LETTER O */ - "p" to 0x0070 to "KEYCODE_P", /* U+0070 LATIN SMALL LETTER P */ - "q" to 0x0071 to "KEYCODE_Q", /* U+0071 LATIN SMALL LETTER Q */ - "r" to 0x0072 to "KEYCODE_R", /* U+0072 LATIN SMALL LETTER R */ - "s" to 0x0073 to "KEYCODE_S", /* U+0073 LATIN SMALL LETTER S */ - "t" to 0x0074 to "KEYCODE_T", /* U+0074 LATIN SMALL LETTER T */ - "u" to 0x0075 to "KEYCODE_U", /* U+0075 LATIN SMALL LETTER U */ - "v" to 0x0076 to "KEYCODE_V", /* U+0076 LATIN SMALL LETTER V */ - "w" to 0x0077 to "KEYCODE_W", /* U+0077 LATIN SMALL LETTER W */ - "x" to 0x0078 to "KEYCODE_X", /* U+0078 LATIN SMALL LETTER X */ - "y" to 0x0079 to "KEYCODE_Y", /* U+0079 LATIN SMALL LETTER Y */ - "z" to 0x007a to "KEYCODE_Z", /* U+007A LATIN SMALL LETTER Z */ + "a" to 0x0061 to "KEYCODE_A", // U+0061 LATIN SMALL LETTER A + "b" to 0x0062 to "KEYCODE_B", // U+0062 LATIN SMALL LETTER B + "c" to 0x0063 to "KEYCODE_C", // U+0063 LATIN SMALL LETTER C + "d" to 0x0064 to "KEYCODE_D", // U+0064 LATIN SMALL LETTER D + "e" to 0x0065 to "KEYCODE_E", // U+0065 LATIN SMALL LETTER E + "f" to 0x0066 to "KEYCODE_F", // U+0066 LATIN SMALL LETTER F + "g" to 0x0067 to "KEYCODE_G", // U+0067 LATIN SMALL LETTER G + "h" to 0x0068 to "KEYCODE_H", // U+0068 LATIN SMALL LETTER H + "i" to 0x0069 to "KEYCODE_I", // U+0069 LATIN SMALL LETTER I + "j" to 0x006a to "KEYCODE_J", // U+006A LATIN SMALL LETTER J + "k" to 0x006b to "KEYCODE_K", // U+006B LATIN SMALL LETTER K + "l" to 0x006c to "KEYCODE_L", // U+006C LATIN SMALL LETTER L + "m" to 0x006d to "KEYCODE_M", // U+006D LATIN SMALL LETTER M + "n" to 0x006e to "KEYCODE_N", // U+006E LATIN SMALL LETTER N + "o" to 0x006f to "KEYCODE_O", // U+006F LATIN SMALL LETTER O + "p" to 0x0070 to "KEYCODE_P", // U+0070 LATIN SMALL LETTER P + "q" to 0x0071 to "KEYCODE_Q", // U+0071 LATIN SMALL LETTER Q + "r" to 0x0072 to "KEYCODE_R", // U+0072 LATIN SMALL LETTER R + "s" to 0x0073 to "KEYCODE_S", // U+0073 LATIN SMALL LETTER S + "t" to 0x0074 to "KEYCODE_T", // U+0074 LATIN SMALL LETTER T + "u" to 0x0075 to "KEYCODE_U", // U+0075 LATIN SMALL LETTER U + "v" to 0x0076 to "KEYCODE_V", // U+0076 LATIN SMALL LETTER V + "w" to 0x0077 to "KEYCODE_W", // U+0077 LATIN SMALL LETTER W + "x" to 0x0078 to "KEYCODE_X", // U+0078 LATIN SMALL LETTER X + "y" to 0x0079 to "KEYCODE_Y", // U+0079 LATIN SMALL LETTER Y + "z" to 0x007a to "KEYCODE_Z", // U+007A LATIN SMALL LETTER Z // 0x007b to KeyEvent.KEYCODE_BRACELEFT, /* U+007B LEFT CURLY BRACKET */ // 0x007c to KeyEvent.KEYCODE_BAR, /* U+007C VERTICAL LINE */ // 0x007d to KeyEvent.KEYCODE_BRACERIGHT, /* U+007D RIGHT CURLY BRACKET */ // 0x007e to KeyEvent.KEYCODE_ASCIITILDE, /* U+007E TILDE */ - - "F1" to 0xffbe to "KEYCODE_F1", - "F2" to 0xffbf to "KEYCODE_F2", - "F3" to 0xffc0 to "KEYCODE_F3", - "F4" to 0xffc1 to "KEYCODE_F4", - "F5" to 0xffc2 to "KEYCODE_F5", - "F6" to 0xffc3 to "KEYCODE_F6", - "F7" to 0xffc4 to "KEYCODE_F7", - "F8" to 0xffc5 to "KEYCODE_F8", - "F9" to 0xffc6 to "KEYCODE_F9", - "F10" to 0xffc7 to "KEYCODE_F10", - "F11" to 0xffc8 to "KEYCODE_F11", - "F12" to 0xffc9 to "KEYCODE_F12", - - "Shift_L" to 0xffe1 to "KEYCODE_SHIFT_LEFT", - "Shift_R" to 0xffe2 to "KEYCODE_SHIFT_RIGHT", - "Control_L" to 0xffe3 to "KEYCODE_CTRL_LEFT", - "Control_R" to 0xffe4 to "KEYCODE_CTRL_RIGHT", - "Caps_Lock" to 0xffe5 to "KEYCODE_CAPS_LOCK", - "Meta_L" to 0xffe7 to "KEYCODE_META_LEFT", - "Meta_R" to 0xffe8 to "KEYCODE_META_RIGHT", - "Alt_L" to 0xffe9 to "KEYCODE_ALT_LEFT", - "Alt_R" to 0xffea to "KEYCODE_ALT_RIGHT", - - "Insert" to 0xff63 to "KEYCODE_INSERT", - "Delete" to 0xffff to "KEYCODE_FORWARD_DEL", // Delete - "Home" to 0xff50 to "KEYCODE_MOVE_HOME", - "End" to 0xff57 to "KEYCODE_MOVE_END", - "Page_Down" to 0xff56 to "KEYCODE_PAGE_DOWN", - "Page_Up" to 0xff55 to "KEYCODE_PAGE_UP", - "Tab" to 0xff09 to "KEYCODE_TAB", - "BackSpace" to 0xff08 to "KEYCODE_DEL", // BackSpace - "Return" to 0xff0d to "KEYCODE_ENTER", - "Escape" to 0xff1b to "KEYCODE_ESCAPE", - - "Up" to 0xff52 to "KEYCODE_DPAD_UP", - "Down" to 0xff54 to "KEYCODE_DPAD_DOWN", - "Left" to 0xff51 to "KEYCODE_DPAD_LEFT", - "Right" to 0xff53 to "KEYCODE_DPAD_RIGHT", - - "KP_Divide" to 0xffaf to "KEYCODE_NUMPAD_DIVIDE", - "KP_Multiply" to 0xffaa to "KEYCODE_NUMPAD_MULTIPLY", - "KP_Subtract" to 0xffad to "KEYCODE_NUMPAD_SUBTRACT", - "KP_7" to 0xffb7 to "KEYCODE_NUMPAD_7", - "KP_8" to 0xffb8 to "KEYCODE_NUMPAD_8", - "KP_9" to 0xffb9 to "KEYCODE_NUMPAD_9", - "KP_Add" to 0xffab to "KEYCODE_NUMPAD_ADD", - "KP_4" to 0xffb4 to "KEYCODE_NUMPAD_4", - "KP_5" to 0xffb5 to "KEYCODE_NUMPAD_5", - "KP_6" to 0xffb6 to "KEYCODE_NUMPAD_6", - "KP_1" to 0xffb1 to "KEYCODE_NUMPAD_1", - "KP_2" to 0xffb2 to "KEYCODE_NUMPAD_2", - "KP_3" to 0xffb3 to "KEYCODE_NUMPAD_3", - "KP_Enter" to 0xff8d to "KEYCODE_NUMPAD_ENTER", - "KP_0" to 0xffb0 to "KEYCODE_NUMPAD_0", - "KP_Decimal" to 0xffae to "KEYCODE_NUMPAD_DOT", - - "Eisu_toggle" to 0xff30 to "KEYCODE_EISU", // RimeKey_Eisu_toggle - "Kana_Lock" to 0xff2d to "KEYCODE_KANA", // RimeKey_Kana_Lock - "Hiragana_Katakana" to 0xff27 to "KEYCODE_KATAKANA_HIRAGANA", // RimeKey_Hiragana_Katakana - "Zenkaku_Hankaku" to 0xff2a to "KEYCODE_ZENKAKU_HANKAKU", // RimeKey_Zenkaku_Hankaku - "VoidSymbol" to 0xffffff to "KEYCODE_UNKNOWN", // RimeKey_VoidSymbol - ) + "F1" to 0xffbe to "KEYCODE_F1", + "F2" to 0xffbf to "KEYCODE_F2", + "F3" to 0xffc0 to "KEYCODE_F3", + "F4" to 0xffc1 to "KEYCODE_F4", + "F5" to 0xffc2 to "KEYCODE_F5", + "F6" to 0xffc3 to "KEYCODE_F6", + "F7" to 0xffc4 to "KEYCODE_F7", + "F8" to 0xffc5 to "KEYCODE_F8", + "F9" to 0xffc6 to "KEYCODE_F9", + "F10" to 0xffc7 to "KEYCODE_F10", + "F11" to 0xffc8 to "KEYCODE_F11", + "F12" to 0xffc9 to "KEYCODE_F12", + "Shift_L" to 0xffe1 to "KEYCODE_SHIFT_LEFT", + "Shift_R" to 0xffe2 to "KEYCODE_SHIFT_RIGHT", + "Control_L" to 0xffe3 to "KEYCODE_CTRL_LEFT", + "Control_R" to 0xffe4 to "KEYCODE_CTRL_RIGHT", + "Caps_Lock" to 0xffe5 to "KEYCODE_CAPS_LOCK", + "Meta_L" to 0xffe7 to "KEYCODE_META_LEFT", + "Meta_R" to 0xffe8 to "KEYCODE_META_RIGHT", + "Alt_L" to 0xffe9 to "KEYCODE_ALT_LEFT", + "Alt_R" to 0xffea to "KEYCODE_ALT_RIGHT", + "Insert" to 0xff63 to "KEYCODE_INSERT", + "Delete" to 0xffff to "KEYCODE_FORWARD_DEL", // Delete + "Home" to 0xff50 to "KEYCODE_MOVE_HOME", + "End" to 0xff57 to "KEYCODE_MOVE_END", + "Page_Down" to 0xff56 to "KEYCODE_PAGE_DOWN", + "Page_Up" to 0xff55 to "KEYCODE_PAGE_UP", + "Tab" to 0xff09 to "KEYCODE_TAB", + "BackSpace" to 0xff08 to "KEYCODE_DEL", // BackSpace + "Return" to 0xff0d to "KEYCODE_ENTER", + "Escape" to 0xff1b to "KEYCODE_ESCAPE", + "Up" to 0xff52 to "KEYCODE_DPAD_UP", + "Down" to 0xff54 to "KEYCODE_DPAD_DOWN", + "Left" to 0xff51 to "KEYCODE_DPAD_LEFT", + "Right" to 0xff53 to "KEYCODE_DPAD_RIGHT", + "KP_Divide" to 0xffaf to "KEYCODE_NUMPAD_DIVIDE", + "KP_Multiply" to 0xffaa to "KEYCODE_NUMPAD_MULTIPLY", + "KP_Subtract" to 0xffad to "KEYCODE_NUMPAD_SUBTRACT", + "KP_7" to 0xffb7 to "KEYCODE_NUMPAD_7", + "KP_8" to 0xffb8 to "KEYCODE_NUMPAD_8", + "KP_9" to 0xffb9 to "KEYCODE_NUMPAD_9", + "KP_Add" to 0xffab to "KEYCODE_NUMPAD_ADD", + "KP_4" to 0xffb4 to "KEYCODE_NUMPAD_4", + "KP_5" to 0xffb5 to "KEYCODE_NUMPAD_5", + "KP_6" to 0xffb6 to "KEYCODE_NUMPAD_6", + "KP_1" to 0xffb1 to "KEYCODE_NUMPAD_1", + "KP_2" to 0xffb2 to "KEYCODE_NUMPAD_2", + "KP_3" to 0xffb3 to "KEYCODE_NUMPAD_3", + "KP_Enter" to 0xff8d to "KEYCODE_NUMPAD_ENTER", + "KP_0" to 0xffb0 to "KEYCODE_NUMPAD_0", + "KP_Decimal" to 0xffae to "KEYCODE_NUMPAD_DOT", + "Eisu_toggle" to 0xff30 to "KEYCODE_EISU", // RimeKey_Eisu_toggle + "Kana_Lock" to 0xff2d to "KEYCODE_KANA", // RimeKey_Kana_Lock + "Hiragana_Katakana" to 0xff27 to "KEYCODE_KATAKANA_HIRAGANA", // RimeKey_Hiragana_Katakana + "Zenkaku_Hankaku" to 0xff2a to "KEYCODE_ZENKAKU_HANKAKU", // RimeKey_Zenkaku_Hankaku + "VoidSymbol" to 0xffffff to "KEYCODE_UNKNOWN", // RimeKey_VoidSymbol + ) override fun process(resolver: Resolver): List { // We don't process annotations at all @@ -205,109 +200,114 @@ internal class GenKeyMappingProcessor(private val environment: SymbolProcessorEn private fun keyName(p: Pair) = "RimeKey_${p.first}" override fun finish() { - val keyCodeFromVal = FunSpec - .builder("valToKeyCode") - .addAnnotation(JvmStatic::class) - .addParameter("val", Int::class) - .returns(Int::class.asTypeName().copy(nullable = false)) - .addCode( - """ + val keyCodeFromVal = + FunSpec + .builder("valToKeyCode") + .addAnnotation(JvmStatic::class) + .addParameter("val", Int::class) + .returns(Int::class.asTypeName().copy(nullable = false)) + .addCode( + """ | return when (`val`) { | ${pairs.joinToString(separator = "\n| ") { (f, code) -> "${keyName(f)} -> KeyEvent.$code" }} | else -> KeyEvent.KEYCODE_UNKNOWN | } - """.trimMargin(), - ) - .build() + """.trimMargin(), + ) + .build() - val keyCodeToVal = FunSpec - .builder("keyCodeToVal") - .addKdoc("Duplicate labels are expected, as the mapping is not one-to-one") - .addAnnotation(JvmStatic::class) - .addParameter("code", Int::class) - .returns(Int::class.asTypeName().copy(nullable = false)) - .addCode( - """ + val keyCodeToVal = + FunSpec + .builder("keyCodeToVal") + .addKdoc("Duplicate labels are expected, as the mapping is not one-to-one") + .addAnnotation(JvmStatic::class) + .addParameter("code", Int::class) + .returns(Int::class.asTypeName().copy(nullable = false)) + .addCode( + """ | return when (code) { | ${ - // exclude uppercase latin letter range because: - // - there is not separate KeyCode for upper and lower case characters - // - ASCII printable characters have same KeySym value as their char code - // - they should produce different KeySym when hold Shift - // TODO: map (keyCode with metaState) to (KeySym with KeyStates) at once - pairs.filter { it.first.second !in 0x41..0x5a } - .joinToString(separator = "\n| ") { (f, code) -> - "KeyEvent.$code -> ${keyName(f)}" - } - } + // exclude uppercase latin letter range because: + // - there is not separate KeyCode for upper and lower case characters + // - ASCII printable characters have same KeySym value as their char code + // - they should produce different KeySym when hold Shift + // TODO: map (keyCode with metaState) to (KeySym with KeyStates) at once + pairs.filter { it.first.second !in 0x41..0x5a } + .joinToString(separator = "\n| ") { (f, code) -> + "KeyEvent.$code -> ${keyName(f)}" + } + } | else -> RimeKey_VoidSymbol | } - """.trimMargin(), - ) - .build() + """.trimMargin(), + ) + .build() - val keyValFromName = FunSpec - .builder("nameToKeyVal") - .addAnnotation(JvmStatic::class) - .addParameter("name", String::class) - .returns(Int::class.asTypeName().copy(nullable = false)) - .addCode( - """ + val keyValFromName = + FunSpec + .builder("nameToKeyVal") + .addAnnotation(JvmStatic::class) + .addParameter("name", String::class) + .returns(Int::class.asTypeName().copy(nullable = false)) + .addCode( + """ | return when (name) { | ${pairs.joinToString(separator = "\n| ") { (f, _) -> "\"${f.first}\" -> ${keyName(f)}" }} | else -> RimeKey_VoidSymbol | } - """.trimMargin(), - ) - .build() + """.trimMargin(), + ) + .build() - val keyValToName = FunSpec - .builder("keyValToName") - .addAnnotation(JvmStatic::class) - .addParameter("val", Int::class) - .returns(String::class.asTypeName().copy(nullable = false)) - .addCode( - """ + val keyValToName = + FunSpec + .builder("keyValToName") + .addAnnotation(JvmStatic::class) + .addParameter("val", Int::class) + .returns(String::class.asTypeName().copy(nullable = false)) + .addCode( + """ | return when (`val`) { | ${pairs.joinToString(separator = "\n| ") { (f, _) -> "${keyName(f)} -> \"${f.first}\"" }} | else -> "VoidSymbol" | } - """.trimMargin(), - ) - .build() + """.trimMargin(), + ) + .build() - val obj = TypeSpec - .objectBuilder("RimeKeyMapping") - .addFunction(keyCodeFromVal) - .addFunction(keyCodeToVal) - .addFunction(keyValFromName) - .addFunction(keyValToName) - .apply { - pairs.forEach { (f, _) -> - val (_, `val`) = f - PropertySpec - .builder( - keyName(f), - Int::class, - KModifier.CONST, - ) - .initializer(String.format("0x%04x", `val`)) - .build() - .let { addProperty(it) } + val obj = + TypeSpec + .objectBuilder("RimeKeyMapping") + .addFunction(keyCodeFromVal) + .addFunction(keyCodeToVal) + .addFunction(keyValFromName) + .addFunction(keyValToName) + .apply { + pairs.forEach { (f, _) -> + val (_, `val`) = f + PropertySpec + .builder( + keyName(f), + Int::class, + KModifier.CONST, + ) + .initializer(String.format("0x%04x", `val`)) + .build() + .let { addProperty(it) } + } } - } - .build() + .build() - val file = FileSpec - .builder("com.osfans.trime.core", "RimeKeyMapping") - .addType(obj) - .addImport("android.view", "KeyEvent") - .build() + val file = + FileSpec + .builder("com.osfans.trime.core", "RimeKeyMapping") + .addType(obj) + .addImport("android.view", "KeyEvent") + .build() file.writeTo(environment.codeGenerator, false) } } class GenKeyMappingProvider : SymbolProcessorProvider { - override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = - GenKeyMappingProcessor(environment) + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = GenKeyMappingProcessor(environment) }