diff --git a/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt b/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt index 2e278f1034..5357615070 100644 --- a/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt +++ b/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt @@ -7,6 +7,7 @@ package com.osfans.trime.daemon import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent +import android.content.Context import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat @@ -21,11 +22,10 @@ import com.osfans.trime.core.lifecycleScope import com.osfans.trime.core.whenReady import com.osfans.trime.ui.main.LogActivity import com.osfans.trime.util.appContext +import com.osfans.trime.util.logcat import com.osfans.trime.util.toast import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -151,9 +151,6 @@ object RimeDaemon { .let { notificationManager.notify(id, it) } realRime.finalize() realRime.startup(fullCheck) - realRime.messageFlow - .onEach(::handleRimeMessage) - .launchIn(TrimeApplication.getInstance().coroutineScope) TrimeApplication.getInstance().coroutineScope.launch { // cancel notification on ready realRime.lifecycle.whenReady { @@ -162,44 +159,45 @@ object RimeDaemon { } } - private suspend fun handleRimeMessage(it: RimeMessage<*>) { + suspend fun onRimeMessage( + ctx: Context, + it: RimeMessage<*>, + ) { if (it is RimeMessage.DeployMessage) { when (it.data) { RimeMessage.DeployMessage.State.Start -> { withContext(Dispatchers.IO) { - Runtime.getRuntime().exec(arrayOf("logcat", "-c")) + logcat { clear() } } } RimeMessage.DeployMessage.State.Success -> { - ContextCompat.getMainExecutor(appContext).execute { - appContext.toast(R.string.deploy_finish) + ContextCompat.getMainExecutor(ctx).execute { + ctx.toast(R.string.deploy_finish) } } RimeMessage.DeployMessage.State.Failure -> { val intent = - Intent(appContext, LogActivity::class.java).apply { + Intent(ctx, LogActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val log = withContext(Dispatchers.IO) { - Runtime - .getRuntime() - .exec( - arrayOf("logcat", "-d", "-v", "brief", "-s", "rime.trime:W"), - ).inputStream - .bufferedReader() - .readText() + logcat { + format("brief") + filterspec("rime.trime", "W") + dump() + }.inputStream.bufferedReader().readText() } putExtra(LogActivity.FROM_DEPLOY, true) putExtra(LogActivity.DEPLOY_FAILURE_TRACE, log) } NotificationCompat - .Builder(appContext, CHANNEL_ID) + .Builder(ctx, CHANNEL_ID) .setSmallIcon(R.drawable.ic_baseline_warning_24) - .setContentTitle(appContext.getString(R.string.rime_daemon)) - .setContentText(appContext.getString(R.string.view_deploy_failure_log)) + .setContentTitle(ctx.getString(R.string.rime_daemon)) + .setContentText(ctx.getString(R.string.view_deploy_failure_log)) .setContentIntent( PendingIntent.getActivity( - appContext, + ctx, 0, intent, PendingIntent.FLAG_ONE_SHOT or 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 5d21e8d3df..f907601aa1 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 @@ -259,7 +259,10 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { } } } - else -> {} + else -> + lifecycleScope.launch { + RimeDaemon.onRimeMessage(this@TrimeInputMethodService, it) + } } } diff --git a/app/src/main/java/com/osfans/trime/util/Flow.kt b/app/src/main/java/com/osfans/trime/util/Flow.kt new file mode 100644 index 0000000000..ae20a9e81a --- /dev/null +++ b/app/src/main/java/com/osfans/trime/util/Flow.kt @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.osfans.trime.util + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.cancellable +import kotlinx.coroutines.flow.flowOn + +fun Process.asFlow(): Flow = + inputStream + .bufferedReader() + .lineSequence() + .asFlow() + .flowOn(Dispatchers.IO) + .cancellable() 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 4e175fe532..3ca38e60a1 100644 --- a/app/src/main/java/com/osfans/trime/util/Logcat.kt +++ b/app/src/main/java/com/osfans/trime/util/Logcat.kt @@ -16,10 +16,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.cancellable -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch class Logcat( @@ -42,12 +39,10 @@ class Logcat( fun getLogAsync(): Deferred>> = async { runCatching { - Runtime - .getRuntime() - .exec(arrayOf("logcat", pid?.let { "--pid=$it" } ?: "", "-d")) - .inputStream - .bufferedReader() - .readLines() + logcat { + pid?.let { pid(it) } + dump() + }.inputStream.bufferedReader().readLines() } } @@ -56,7 +51,7 @@ class Logcat( */ fun clearLog(): Job = launch { - runCatching { Runtime.getRuntime().exec(arrayOf("logcat", "-c")) } + runCatching { logcat { clear() } } } /** @@ -68,16 +63,11 @@ class Logcat( } else { launch { runCatching { - Runtime - .getRuntime() - .exec(arrayOf("logcat", pid?.let { "--pid=$it" } ?: "", "-v", "time")) - .also { process = it } - .inputStream - .bufferedReader() - .lineSequence() + logcat { + pid?.let { pid(it) } + format("time") + }.also { process = it } .asFlow() - .flowOn(Dispatchers.IO) - .cancellable() .collect { flow.emit(it) } } }.also { emittingJob = it } @@ -95,3 +85,35 @@ class Logcat( val default by lazy { Logcat() } } } + +// DSL +inline fun logcat(builderAction: LogcatCommandBuilder.() -> Unit): java.lang.Process = + LogcatCommandBuilder().apply(builderAction).toProcess() + +class LogcatCommandBuilder { + private val cmdList = arrayListOf("logcat") + + fun clear() = apply { cmdList.add("--clear") } + + fun dump() = apply { cmdList.add("-d") } + + fun pid(pid: Int) = apply { cmdList.add("--pid=$pid") } + + fun format(format: String) = apply { cmdList.add("--format=$format") } + + fun filterspec( + tag: String, + priority: String, + ) = apply { + cmdList.add("-s") + cmdList.add("$tag:$priority") + } + + fun filterspec(spec: String) = + apply { + cmdList.add("-s") + cmdList.add(spec) + } + + fun toProcess(): java.lang.Process = Runtime.getRuntime().exec(cmdList.toTypedArray()) +}