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 8ec71a9789..e3b1d76853 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 @@ -5,7 +5,6 @@ package com.osfans.trime.data.theme import com.osfans.trime.core.Rime -import com.osfans.trime.data.prefs.AppPrefs import com.osfans.trime.data.theme.mapper.GeneralStyleMapper import com.osfans.trime.data.theme.model.GeneralStyle import com.osfans.trime.util.config.Config @@ -32,7 +31,6 @@ class Theme( private set companion object { - private val prefs = AppPrefs.defaultInstance().theme private const val VERSION_KEY = "config_version" private const val DEFAULT_THEME_NAME = "trime" @@ -70,7 +68,6 @@ class Theme( presetColorSchemes = config.getMap("preset_color_schemes") presetKeyboards = config.getMap("preset_keyboards") Timber.i("The theme is initialized") - prefs.selectedTheme = name } private fun mapToGeneralStyle(config: Config): GeneralStyle { 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 4d972b780d..394a0b9b4d 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 @@ -8,6 +8,7 @@ import androidx.annotation.Keep import com.osfans.trime.data.base.DataManager import com.osfans.trime.data.prefs.AppPrefs import com.osfans.trime.ime.symbol.TabManager +import com.osfans.trime.util.WeakHashSet import java.io.File object ThemeManager { @@ -50,23 +51,50 @@ object ThemeManager { setNormalTheme(prefs.selectedTheme) } - lateinit var activeTheme: Theme - private set + private lateinit var _activeTheme: Theme + + var activeTheme: Theme + get() = _activeTheme + set(value) { + if (::_activeTheme.isInitialized && _activeTheme == value) return + _activeTheme = value + fireChange() + } + + private val onChangeListeners = WeakHashSet() + + fun addOnChangedListener(listener: OnThemeChangeListener) { + onChangeListeners.add(listener) + } + + fun removeOnChangedListener(listener: OnThemeChangeListener) { + onChangeListeners.remove(listener) + } + + private fun fireChange() { + onChangeListeners.forEach { it.onThemeChange(_activeTheme) } + } private val prefs = AppPrefs.defaultInstance().theme - fun init() = setNormalTheme(prefs.selectedTheme) + fun init() { + Theme(prefs.selectedTheme).let { + EventManager.resetCache() + FontManager.resetCache(it) + ColorManager.resetCache(it) + TabManager.resetCache(it) + _activeTheme = it + } + } fun setNormalTheme(name: String) { Theme(name).let { - if (::activeTheme.isInitialized) { - if (activeTheme == it) return - } EventManager.resetCache() FontManager.resetCache(it) ColorManager.resetCache(it) TabManager.resetCache(it) activeTheme = it } + prefs.selectedTheme = name } } diff --git a/app/src/main/java/com/osfans/trime/ime/core/InputView.kt b/app/src/main/java/com/osfans/trime/ime/core/InputView.kt index 3376705fb2..60d729a25f 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/InputView.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/InputView.kt @@ -51,6 +51,7 @@ import splitties.views.dsl.constraintlayout.constraintLayout import splitties.views.dsl.constraintlayout.endOfParent import splitties.views.dsl.constraintlayout.endToStartOf import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints import splitties.views.dsl.constraintlayout.startOfParent import splitties.views.dsl.constraintlayout.startToEndOf import splitties.views.dsl.constraintlayout.topOfParent @@ -236,7 +237,7 @@ class InputView( ) add( windowManager.view, - lParams(matchParent, wrapContent) { + lParams(matchConstraints, wrapContent) { below(quickBar.view) above(bottomPaddingSpace) }, diff --git a/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt b/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt index 914dd99478..e4b5e1859a 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt @@ -29,6 +29,7 @@ import android.view.inputmethod.ExtractedTextRequest import android.widget.FrameLayout import android.widget.LinearLayout import androidx.annotation.Keep +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope @@ -76,8 +77,6 @@ import splitties.systemservices.inputMethodManager import splitties.views.gravityBottom import timber.log.Timber import java.util.Locale -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext /** [輸入法][InputMethodService]主程序 */ @@ -102,6 +101,12 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { var lastCommittedText: CharSequence = "" private set + @Keep + private val onThemeChangeListener = + ThemeManager.OnThemeChangeListener { + recreateInputView(it) + } + @Keep private val onColorChangeListener = ColorManager.OnColorChangeListener { @@ -111,11 +116,10 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { } private fun postJob( - ctx: CoroutineContext, scope: CoroutineScope, block: suspend () -> Unit, ): Job { - val job = scope.launch(ctx, CoroutineStart.LAZY) { block() } + val job = scope.launch(start = CoroutineStart.LAZY) { block() } jobs.trySend(job) return job } @@ -127,10 +131,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { * subsequent operations can start if the prior operation is not finished (suspended), * [postRimeJob] ensures that operations are executed sequentially. */ - fun postRimeJob( - ctx: CoroutineContext = EmptyCoroutineContext, - block: suspend RimeApi.() -> Unit, - ) = postJob(ctx, rime.lifecycleScope) { rime.runOnReady(block) } + fun postRimeJob(block: suspend RimeApi.() -> Unit) = postJob(rime.lifecycleScope) { rime.runOnReady(block) } override fun onWindowShown() { super.onWindowShown() @@ -222,6 +223,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { handleRimeResponse(it) } } + ThemeManager.addOnChangedListener(onThemeChangeListener) ColorManager.addOnChangedListener(onColorChangeListener) super.onCreate() instance = this @@ -324,6 +326,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { mIntentReceiver = null InputFeedbackManager.destroy() inputView = null + ThemeManager.removeOnChangedListener(onThemeChangeListener) ColorManager.removeOnChangedListener(onColorChangeListener) super.onDestroy() RimeDaemon.destroySession(javaClass.name) @@ -413,8 +416,10 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { override fun onCreateInputView(): View { Timber.d("onCreateInputView") - postRimeJob(Dispatchers.Main) { - recreateInputView(ThemeManager.activeTheme) + postRimeJob { + ContextCompat.getMainExecutor(this@TrimeInputMethodService).execute { + recreateInputView(ThemeManager.activeTheme) + } } initializationUi = InitializationUi(this) return initializationUi!!.root @@ -460,7 +465,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { restarting: Boolean, ) { Timber.d("onStartInputView: restarting=%s", restarting) - postRimeJob(Dispatchers.Main) { + postRimeJob { InputFeedbackManager.loadSoundEffects(this@TrimeInputMethodService) InputFeedbackManager.resetPlayProgress() isComposable = @@ -477,8 +482,9 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { if (prefs.other.showStatusBarIcon) { showStatusIcon(R.drawable.ic_trime_status) // 狀態欄圖標 } - setCandidatesViewShown(!rime.run { isEmpty() }) // 軟鍵盤出現時顯示候選欄 - inputView?.startInput(attribute, restarting) + ContextCompat.getMainExecutor(this@TrimeInputMethodService).execute { + inputView?.startInput(attribute, restarting) + } when (attribute.inputType and InputType.TYPE_MASK_VARIATION) { InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, InputType.TYPE_TEXT_VARIATION_PASSWORD, diff --git a/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardWindow.kt b/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardWindow.kt index 63f3d81cef..888ba4d4c2 100644 --- a/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardWindow.kt +++ b/app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardWindow.kt @@ -12,6 +12,7 @@ import android.view.View import android.view.inputmethod.EditorInfo import android.widget.FrameLayout import androidx.core.content.ContextCompat +import com.osfans.trime.R import com.osfans.trime.core.Rime import com.osfans.trime.core.RimeNotification.OptionNotification import com.osfans.trime.daemon.RimeSession @@ -76,7 +77,7 @@ class KeyboardWindow( private val currentKeyboard: Keyboard? get() = keyboardsCached[currentKeyboardId] override fun onCreateView(): View { - keyboardView = context.frameLayout() + keyboardView = context.frameLayout(R.id.keyboard_view) attachKeyboard(evalKeyboard(".default")) keyboardView.apply { add(mainKeyboardView, lParams(matchParent, matchParent)) } return keyboardView diff --git a/app/src/main/java/com/osfans/trime/ime/window/BoardWindowManager.kt b/app/src/main/java/com/osfans/trime/ime/window/BoardWindowManager.kt index 383c4a0ea3..20b48d85bc 100644 --- a/app/src/main/java/com/osfans/trime/ime/window/BoardWindowManager.kt +++ b/app/src/main/java/com/osfans/trime/ime/window/BoardWindowManager.kt @@ -10,6 +10,7 @@ import android.widget.FrameLayout import androidx.transition.Transition import androidx.transition.TransitionManager import androidx.transition.TransitionSet +import com.osfans.trime.R import com.osfans.trime.ime.broadcast.InputBroadcaster import com.osfans.trime.ime.dependency.InputScope import me.tatarka.inject.annotations.Inject @@ -60,6 +61,7 @@ class BoardWindowManager( throw IllegalStateException("${window.key} is already occupied") } } + broadcaster.addReceiver(window) val view = if (createView) window.onCreateView() else null cachedResidentWindows[window.key] = window to view } @@ -111,7 +113,7 @@ class BoardWindowManager( broadcaster.onWindowAttached(window) } - val view: FrameLayout by lazy { context.frameLayout() } + val view: FrameLayout by lazy { context.frameLayout(R.id.input_window) } fun isAttached(window: BoardWindow) = currentWindow === window } diff --git a/app/src/main/res/values/input_view_ids.xml b/app/src/main/res/values/input_view_ids.xml index 48c8caebcf..e27faa8649 100644 --- a/app/src/main/res/values/input_view_ids.xml +++ b/app/src/main/res/values/input_view_ids.xml @@ -9,4 +9,6 @@ SPDX-License-Identifier: GPL-3.0-or-later + +