diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt index 4ebcf5d8..3a2aa0b4 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt @@ -11,7 +11,6 @@ import androidx.core.content.ContextCompat import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.fcitx.fcitx5.android.FcitxApplication @@ -485,7 +484,7 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { @Keep private val onClipboardUpdate = ClipboardManager.OnClipboardUpdateListener { - lifecycle.lifecycleScope.launch { setClipboard(it.text, it.sensitive) } + lifecycle.launchWhenReady { setClipboard(it.text, it.sensitive) } } private fun computeAddonGraph() = runBlocking { @@ -506,7 +505,7 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { // this method runs in same thread with `startupFcitx` // block it will also block fcitx onFirstRun() - unregisterFcitxEventHandler(::handleFcitxEvent) + unregisterFcitxEventHandler(::handleFirstRunReadyEvent) } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt index 4ea37fe8..33fa37cb 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt @@ -5,69 +5,67 @@ package org.fcitx.fcitx5.android.core import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch -import kotlinx.coroutines.suspendCancellableCoroutine -import java.util.concurrent.ConcurrentLinkedQueue -import kotlin.coroutines.Continuation -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.resume class FcitxLifecycleRegistry : FcitxLifecycle { - private val observers = ConcurrentLinkedQueue() + private val internalStateFlow = MutableStateFlow(FcitxLifecycle.State.STOPPED) - override fun addObserver(observer: FcitxLifecycleObserver) { - observers.add(observer) - } - - override fun removeObserver(observer: FcitxLifecycleObserver) { - observers.remove(observer) - } + override val stateFlow = internalStateFlow.asStateFlow() override val currentState: FcitxLifecycle.State - get() = internalState + get() = internalStateFlow.value - private var internalState = FcitxLifecycle.State.STOPPED + private val job = SupervisorJob() - override val lifecycleScope: CoroutineScope = - FcitxLifecycleCoroutineScope(this).also { addObserver(it) } + override val lifecycleScope = CoroutineScope(job + Dispatchers.Default) - fun postEvent(event: FcitxLifecycle.Event) = synchronized(internalState) { - when (event) { - FcitxLifecycle.Event.ON_START -> { - ensureAt(FcitxLifecycle.State.STOPPED) - internalState = FcitxLifecycle.State.STARTING - } - FcitxLifecycle.Event.ON_READY -> { - ensureAt(FcitxLifecycle.State.STARTING) - internalState = FcitxLifecycle.State.READY - } - FcitxLifecycle.Event.ON_STOP -> { - ensureAt(FcitxLifecycle.State.READY) - internalState = FcitxLifecycle.State.STOPPING - } - FcitxLifecycle.Event.ON_STOPPED -> { - ensureAt(FcitxLifecycle.State.STOPPING) - internalState = FcitxLifecycle.State.STOPPED + fun postEvent(event: FcitxLifecycle.Event) { + val newState = internalStateFlow.updateAndGet { + when (event) { + FcitxLifecycle.Event.ON_START -> { + ensureAt(it, FcitxLifecycle.State.STOPPED) + FcitxLifecycle.State.STARTING + } + FcitxLifecycle.Event.ON_READY -> { + ensureAt(it, FcitxLifecycle.State.STARTING) + FcitxLifecycle.State.READY + } + FcitxLifecycle.Event.ON_STOP -> { + ensureAt(it, FcitxLifecycle.State.READY) + FcitxLifecycle.State.STOPPING + } + FcitxLifecycle.Event.ON_STOPPED -> { + ensureAt(it, FcitxLifecycle.State.STOPPING) + FcitxLifecycle.State.STOPPED + } } } - observers.forEach { it.onStateChanged(event) } + if (newState >= FcitxLifecycle.State.STOPPING) { + job.cancelChildren() + } } - private fun ensureAt(state: FcitxLifecycle.State) = takeIf { (currentState == state) } - ?: throw IllegalStateException("Currently not at $state!") - + private fun ensureAt(currentState: FcitxLifecycle.State, state: FcitxLifecycle.State) { + if (currentState != state) { + throw IllegalStateException("Currently not at $state!") + } + } } interface FcitxLifecycle { + val stateFlow: StateFlow val currentState: State val lifecycleScope: CoroutineScope - fun addObserver(observer: FcitxLifecycleObserver) - fun removeObserver(observer: FcitxLifecycleObserver) - enum class State { STARTING, READY, @@ -81,7 +79,6 @@ interface FcitxLifecycle { ON_STOP, ON_STOPPED } - } interface FcitxLifecycleOwner { @@ -91,27 +88,13 @@ interface FcitxLifecycleOwner { val FcitxLifecycleOwner.lifeCycleScope get() = lifecycle.lifecycleScope -fun interface FcitxLifecycleObserver { - fun onStateChanged(event: FcitxLifecycle.Event) -} - -class FcitxLifecycleCoroutineScope( - val lifecycle: FcitxLifecycle, - override val coroutineContext: CoroutineContext = SupervisorJob() -) : CoroutineScope, FcitxLifecycleObserver { - override fun onStateChanged(event: FcitxLifecycle.Event) { - if (lifecycle.currentState >= FcitxLifecycle.State.STOPPING) { - coroutineContext.cancelChildren() - } - } -} - -suspend fun FcitxLifecycle.whenAtState( +suspend inline fun FcitxLifecycle.whenAtState( state: FcitxLifecycle.State, block: suspend CoroutineScope.() -> T -): T = - if (state == currentState) block(lifecycleScope) - else AtStateHelper(this, state).run(block) +): T { + stateFlow.first { it == state } + return block(lifecycleScope) +} suspend inline fun FcitxLifecycle.whenReady(noinline block: suspend CoroutineScope.() -> T) = whenAtState(FcitxLifecycle.State.READY, block) @@ -124,22 +107,3 @@ fun FcitxLifecycle.launchWhenReady(block: suspend CoroutineScope.() -> T) = fun FcitxLifecycle.launchWhenStopped(block: suspend CoroutineScope.() -> T) = lifecycleScope.launch { whenStopped(block) } - -private class AtStateHelper(val lifecycle: FcitxLifecycle, val state: FcitxLifecycle.State) { - private val observer = FcitxLifecycleObserver { - if (lifecycle.currentState == state) - continuation?.resume(Unit) - } - - init { - lifecycle.addObserver(observer) - } - - private var continuation: Continuation? = null - - suspend fun run(block: suspend CoroutineScope.() -> T): T { - suspendCancellableCoroutine { continuation = it } - lifecycle.removeObserver(observer) - return block(lifecycle.lifecycleScope) - } -}