Skip to content

Commit

Permalink
feat: allow users to trigger rime action via adb shell
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiredPlanck committed Jan 30, 2025
1 parent 7e59896 commit 6a95f6c
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 155 deletions.
47 changes: 38 additions & 9 deletions app/src/main/java/com/osfans/trime/TrimeApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import android.app.Application
import android.content.Intent
import android.os.Process
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.osfans.trime.data.db.ClipboardHelper
import com.osfans.trime.data.db.CollectionHelper
import com.osfans.trime.data.db.DraftHelper
import com.osfans.trime.data.prefs.AppPrefs
import com.osfans.trime.receiver.RimeIntentReceiver
import com.osfans.trime.ui.main.LogActivity
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.MainScope
Expand All @@ -28,19 +30,21 @@ import kotlin.system.exitProcess
* classes everywhere.
*/
class TrimeApplication : Application() {
companion object {
private var instance: TrimeApplication? = null
private var lastPid: Int? = null
val coroutineScope = MainScope() + CoroutineName("TrimeApplication")

fun getInstance() = instance ?: throw IllegalStateException("Trime application is not created!")
private val rimeIntentReceiver = RimeIntentReceiver()

fun getLastPid() = lastPid

private const val MAX_STACKTRACE_SIZE = 128000
private fun registerBroadcastReceiver() {
ContextCompat.registerReceiver(
this,
rimeIntentReceiver,
RimeIntentReceiver.intentFilter,
PERMISSION_TEST_INPUT_METHOD,
null,
ContextCompat.RECEIVER_EXPORTED,
)
}

val coroutineScope = MainScope() + CoroutineName("TrimeApplication")

override fun onCreate() {
super.onCreate()
if (!BuildConfig.DEBUG) {
Expand Down Expand Up @@ -128,9 +132,34 @@ class TrimeApplication : Application() {
ClipboardHelper.init(applicationContext)
CollectionHelper.init(applicationContext)
DraftHelper.init(applicationContext)
registerBroadcastReceiver()
} catch (e: Exception) {
e.fillInStackTrace()
return
}
}

companion object {
private var instance: TrimeApplication? = null
private var lastPid: Int? = null

fun getInstance() = instance ?: throw IllegalStateException("Trime application is not created!")

fun getLastPid() = lastPid

private const val MAX_STACKTRACE_SIZE = 128000

/**
* This permission is requested by com.android.shell, makes it possible to start
* deploy from `adb shell am` command:
* ```sh
* adb shell am broadcast -a com.osfans.trime.action.DEPLOY
* ```
* https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-7.0.0_r1/packages/Shell/AndroidManifest.xml#67
*
* other candidate: android.permission.TEST_INPUT_METHOD requires Android 14
* https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-14.0.0_r1/packages/Shell/AndroidManifest.xml#628
*/
const val PERMISSION_TEST_INPUT_METHOD = "android.permission.READ_INPUT_STATE"
}
}
5 changes: 5 additions & 0 deletions app/src/main/java/com/osfans/trime/core/Rime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ class Rime :
getCurrentRimeSchema() == ".default" // 無方案
}

override suspend fun syncUserData(): Boolean =
withRimeContext {
syncRimeUserData()
}

override suspend fun processKey(
value: Int,
modifiers: UInt,
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/osfans/trime/core/RimeApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ interface RimeApi {

suspend fun isEmpty(): Boolean

suspend fun syncUserData(): Boolean

suspend fun processKey(
value: Int,
modifiers: UInt = 0u,
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ object RimeDaemon {
}
}

/**
* Reuse a session for remote service
*/
fun getFirstSessionOrNull() = sessions.firstNotNullOfOrNull { it.value }

private const val CHANNEL_ID = "rime-daemon"
private const val MESSAGE_ID = 2331
private var restartId = 0
Expand Down
138 changes: 0 additions & 138 deletions app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import com.osfans.trime.data.prefs.PreferenceDelegateProvider
import com.osfans.trime.data.theme.ColorManager
import com.osfans.trime.data.theme.Theme
import com.osfans.trime.data.theme.ThemeManager
import com.osfans.trime.ime.broadcast.IntentReceiver
import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode
import com.osfans.trime.ime.candidates.suggestion.InlineSuggestionHandler
import com.osfans.trime.ime.composition.CandidatesView
Expand Down Expand Up @@ -92,7 +91,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
private var candidatesView: CandidatesView? = null
private val inputDeviceManager = InputDeviceManager()
private var initializationUi: InitializationUi? = null
private var mIntentReceiver: IntentReceiver? = null
private val locales = Array(2) { Locale.getDefault() }

private lateinit var inlineSuggestionHandler: InlineSuggestionHandler
Expand Down Expand Up @@ -212,10 +210,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
// could crash
// and lead to a crash loop
Timber.d("onCreate")
mIntentReceiver =
IntentReceiver().also {
it.registerReceiver(this)
}
postRimeJob {
ColorManager.init(resources.configuration)
ThemeManager.init()
Expand Down Expand Up @@ -334,8 +328,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
}

override fun onDestroy() {
mIntentReceiver?.unregisterReceiver(this)
mIntentReceiver = null
InputFeedbackManager.destroy()
inputView = null
recreateInputViewPrefs.forEach {
Expand Down
46 changes: 46 additions & 0 deletions app/src/main/java/com/osfans/trime/receiver/RimeIntentReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2015 - 2025 Rime community
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.osfans.trime.receiver

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.osfans.trime.BuildConfig
import com.osfans.trime.daemon.RimeDaemon
import timber.log.Timber

class RimeIntentReceiver : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent,
) {
val rime = RimeDaemon.getFirstSessionOrNull() ?: return Timber.w("No active rime session, skipping")
Timber.i("Received broadcast ${intent.action}")
when (intent.action) {
ACTION_DEPLOY -> {
Timber.i("try to start maintenance ...")
RimeDaemon.restartRime(true)
}
ACTION_SYNC_USER_DATA -> {
Timber.i("try to sync rime user data ...")
rime.run { syncUserData() }
}
else -> {}
}
}

companion object {
private const val ACTION_DEPLOY = "${BuildConfig.APPLICATION_ID}.action.DEPLOY"
private const val ACTION_SYNC_USER_DATA = "${BuildConfig.APPLICATION_ID}.action.SYNC_USER_DATA"

val intentFilter =
IntentFilter().apply {
addAction(ACTION_DEPLOY)
addAction(ACTION_SYNC_USER_DATA)
}
}
}

0 comments on commit 6a95f6c

Please sign in to comment.