@@ -39,13 +39,16 @@ import com.osfans.trime.core.KeyValue
39
39
import com.osfans.trime.core.Rime
40
40
import com.osfans.trime.core.RimeApi
41
41
import com.osfans.trime.core.RimeKeyMapping
42
+ import com.osfans.trime.core.RimeNotification
42
43
import com.osfans.trime.core.RimeProto
43
44
import com.osfans.trime.core.RimeResponse
44
45
import com.osfans.trime.daemon.RimeDaemon
45
46
import com.osfans.trime.daemon.RimeSession
46
47
import com.osfans.trime.data.db.DraftHelper
47
48
import com.osfans.trime.data.prefs.AppPrefs
49
+ import com.osfans.trime.data.schema.SchemaManager
48
50
import com.osfans.trime.data.theme.ColorManager
51
+ import com.osfans.trime.data.theme.EventManager
49
52
import com.osfans.trime.data.theme.ThemeManager
50
53
import com.osfans.trime.ime.broadcast.IntentReceiver
51
54
import com.osfans.trime.ime.enums.FullscreenMode
@@ -56,10 +59,8 @@ import com.osfans.trime.ime.keyboard.InitializationUi
56
59
import com.osfans.trime.ime.keyboard.InputFeedbackManager
57
60
import com.osfans.trime.ime.symbol.SymbolBoardType
58
61
import com.osfans.trime.ime.symbol.TabManager
59
- import com.osfans.trime.ime.text.TextInputManager
60
62
import com.osfans.trime.util.ShortcutUtils
61
63
import com.osfans.trime.util.ShortcutUtils.openCategory
62
- import com.osfans.trime.util.WeakHashSet
63
64
import com.osfans.trime.util.findSectionFrom
64
65
import com.osfans.trime.util.isLandscape
65
66
import com.osfans.trime.util.isNightMode
@@ -75,6 +76,7 @@ import splitties.bitflags.hasFlag
75
76
import splitties.systemservices.inputMethodManager
76
77
import splitties.views.gravityBottom
77
78
import timber.log.Timber
79
+ import java.util.Locale
78
80
import kotlin.coroutines.CoroutineContext
79
81
import kotlin.coroutines.EmptyCoroutineContext
80
82
@@ -91,10 +93,10 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
91
93
private val commonKeyboardActionListener: CommonKeyboardActionListener ?
92
94
get() = inputView?.commonKeyboardActionListener
93
95
private var initializationUi: InitializationUi ? = null
94
- private var eventListeners = WeakHashSet <EventListener >()
95
96
private var mIntentReceiver: IntentReceiver ? = null
96
97
private var isWindowShown = false // 键盘窗口是否已显示
97
- private var textInputManager: TextInputManager ? = null // 文字输入管理器
98
+ private var isComposable: Boolean = false
99
+ private val locales = Array (2 ) { Locale .getDefault() }
98
100
99
101
var shouldUpdateRimeOption = false
100
102
@@ -131,15 +133,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
131
133
block : suspend RimeApi .() -> Unit ,
132
134
) = postJob(ctx, rime.lifecycleScope) { rime.runOnReady(block) }
133
135
134
- init {
135
- try {
136
- check(self == null ) { " Trime is already initialized" }
137
- self = this
138
- } catch (e: Exception ) {
139
- Timber .e(e)
140
- }
141
- }
142
-
143
136
override fun onWindowShown () {
144
137
super .onWindowShown()
145
138
if (isWindowShown) {
@@ -154,9 +147,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
154
147
withContext(Dispatchers .Main ) {
155
148
updateComposing()
156
149
}
157
- for (listener in eventListeners) {
158
- listener.onWindowShown()
159
- }
160
150
}
161
151
}
162
152
}
@@ -175,9 +165,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
175
165
msg.obj = this
176
166
syncBackgroundHandler.sendMessageDelayed(msg, 5000 ) // 输入面板隐藏5秒后,开始后台同步
177
167
}
178
- for (listener in eventListeners) {
179
- listener.onWindowHidden()
180
- }
181
168
}
182
169
183
170
private fun updateRimeOption (): Boolean {
@@ -226,12 +213,18 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
226
213
lifecycleScope.launch {
227
214
jobs.consumeEach { it.join() }
228
215
}
216
+ lifecycleScope.launch {
217
+ rime.run { notificationFlow }.collect {
218
+ handleRimeNotification(it)
219
+ }
220
+ }
229
221
lifecycleScope.launch {
230
222
rime.run { responseFlow }.collect {
231
223
handleRimeResponse(it)
232
224
}
233
225
}
234
226
super .onCreate()
227
+ instance = this
235
228
// MUST WRAP all code within Service onCreate() in try..catch to prevent any crash loops
236
229
try {
237
230
// Additional try..catch wrapper as the event listeners chain or the super.onCreate() method
@@ -244,26 +237,65 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
244
237
it.registerReceiver(this )
245
238
}
246
239
postRimeJob {
247
- Timber .d(" Running Trime.onCreate" )
248
240
ColorManager .init (resources.configuration)
249
- textInputManager = TextInputManager (this @TrimeInputMethodService, rime)
250
241
InputFeedbackManager .init ()
251
242
restartSystemStartTimingSync()
252
243
shouldUpdateRimeOption = true
253
- try {
254
- for (listener in eventListeners) {
255
- listener.onCreate()
244
+ val theme = ThemeManager .activeTheme
245
+ val defaultLocale = theme.generalStyle.locale.split(DELIMITER_SPLITTER )
246
+ locales[0 ] =
247
+ when (defaultLocale.size) {
248
+ 3 -> Locale (defaultLocale[0 ], defaultLocale[1 ], defaultLocale[2 ])
249
+ 2 -> Locale (defaultLocale[0 ], defaultLocale[1 ])
250
+ else -> Locale .getDefault()
251
+ }
252
+
253
+ val latinLocale = theme.generalStyle.latinLocale.split(DELIMITER_SPLITTER )
254
+ locales[1 ] =
255
+ when (latinLocale.size) {
256
+ 3 -> Locale (latinLocale[0 ], latinLocale[1 ], latinLocale[2 ])
257
+ 2 -> Locale (latinLocale[0 ], latinLocale[1 ])
258
+ else -> Locale .US
256
259
}
257
- } catch (e: Exception ) {
258
- Timber .e(e)
259
- }
260
260
Timber .d(" Trime.onCreate completed" )
261
261
}
262
262
} catch (e: Exception ) {
263
263
Timber .e(e)
264
264
}
265
265
}
266
266
267
+ private fun handleRimeNotification (notification : RimeNotification <* >) {
268
+ if (notification is RimeNotification .SchemaNotification ) {
269
+ SchemaManager .init (notification.value.id)
270
+ recreateInputView()
271
+ inputView?.switchBoard(InputView .Board .Main )
272
+ } else if (notification is RimeNotification .OptionNotification ) {
273
+ val value = notification.value.value
274
+ when (val option = notification.value.option) {
275
+ " ascii_mode" -> {
276
+ InputFeedbackManager .ttsLanguage =
277
+ locales[if (value) 1 else 0 ]
278
+ }
279
+ " _hide_bar" ,
280
+ " _hide_candidate" ,
281
+ -> {
282
+ setCandidatesViewShown(isComposable && ! value)
283
+ }
284
+ " _liquid_keyboard" -> selectLiquidKeyboard(0 )
285
+ else ->
286
+ if (option.startsWith(" _key_" ) && option.length > 5 && value) {
287
+ shouldUpdateRimeOption = false // 防止在 handleRimeNotification 中 setOption
288
+ val key = option.substring(5 )
289
+ inputView
290
+ ?.commonKeyboardActionListener
291
+ ?.listener
292
+ ?.onEvent(EventManager .getEvent(key))
293
+ shouldUpdateRimeOption = true
294
+ }
295
+ }
296
+ }
297
+ }
298
+
267
299
private fun handleRimeResponse (response : RimeResponse ) {
268
300
val (commit, ctx, _) = response
269
301
if (commit != null && ! commit.text.isNullOrEmpty()) {
@@ -282,10 +314,10 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
282
314
if (asciiPunch) Rime .setOption(" ascii_punct" , false )
283
315
commonKeyboardActionListener?.listener?.onText(" {Escape}$text " )
284
316
if (asciiPunch) Rime .setOption(" ascii_punct" , true )
285
- self !! . selectLiquidKeyboard(- 1 )
317
+ selectLiquidKeyboard(- 1 )
286
318
}
287
319
288
- fun selectLiquidKeyboard (tabIndex : Int ) {
320
+ private fun selectLiquidKeyboard (tabIndex : Int ) {
289
321
if (inputView == null ) return
290
322
if (tabIndex >= 0 ) {
291
323
inputView!! .switchBoard(InputView .Board .Symbol )
@@ -309,7 +341,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
309
341
}
310
342
}
311
343
312
- fun selectLiquidKeyboard (type : SymbolBoardType ) {
344
+ private fun selectLiquidKeyboard (type : SymbolBoardType ) {
313
345
selectLiquidKeyboard(TabManager .tabTags.indexOfFirst { it.type == type })
314
346
}
315
347
@@ -333,14 +365,10 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
333
365
mIntentReceiver = null
334
366
InputFeedbackManager .destroy()
335
367
inputView = null
336
- for (listener in eventListeners) {
337
- listener.onDestroy()
338
- }
339
- eventListeners.clear()
340
368
ColorManager .removeOnChangedListener(onColorChangeListener)
341
369
super .onDestroy()
342
370
RimeDaemon .destroySession(javaClass.name)
343
- self = null
371
+ instance = null
344
372
}
345
373
346
374
private fun handleReturnKey () {
@@ -476,9 +504,18 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
476
504
postRimeJob(Dispatchers .Main ) {
477
505
InputFeedbackManager .loadSoundEffects(this @TrimeInputMethodService)
478
506
InputFeedbackManager .resetPlayProgress()
479
- for (listener in eventListeners) {
480
- listener.onStartInputView(attribute, restarting)
481
- }
507
+ selectLiquidKeyboard(- 1 )
508
+ isComposable =
509
+ arrayOf(
510
+ InputType .TYPE_TEXT_VARIATION_SHORT_MESSAGE ,
511
+ InputType .TYPE_TEXT_VARIATION_EMAIL_ADDRESS ,
512
+ InputType .TYPE_TEXT_VARIATION_PASSWORD ,
513
+ InputType .TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ,
514
+ InputType .TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ,
515
+ InputType .TYPE_TEXT_VARIATION_WEB_PASSWORD ,
516
+ ).none { it == attribute.inputType and InputType .TYPE_MASK_VARIATION }
517
+ isComposable = isComposable && ! rime.run { isEmpty() }
518
+ updateComposing()
482
519
if (prefs.other.showStatusBarIcon) {
483
520
showStatusIcon(R .drawable.ic_trime_status) // 狀態欄圖標
484
521
}
@@ -1001,7 +1038,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
1001
1038
/* * 更新Rime的中西文狀態、編輯區文本 */
1002
1039
fun updateComposing () {
1003
1040
inputView?.updateComposing(currentInputConnection)
1004
- if (! onEvaluateInputViewShown()) setCandidatesViewShown(textInputManager !! . isComposable) // 實體鍵盤打字時顯示候選欄
1041
+ if (! onEvaluateInputViewShown()) setCandidatesViewShown(isComposable) // 實體鍵盤打字時顯示候選欄
1005
1042
}
1006
1043
1007
1044
/* *
@@ -1075,32 +1112,16 @@ open class TrimeInputMethodService : LifecycleInputMethodService() {
1075
1112
}
1076
1113
}
1077
1114
1078
- fun addEventListener (listener : EventListener ): Boolean = eventListeners.add(listener)
1079
-
1080
- fun removeEventListener (listener : EventListener ): Boolean = eventListeners.remove(listener)
1081
-
1082
- interface EventListener {
1083
- fun onCreate () {}
1084
-
1085
- fun onDestroy () {}
1086
-
1087
- fun onStartInputView (
1088
- info : EditorInfo ,
1089
- restarting : Boolean ,
1090
- ) {}
1091
-
1092
- fun onWindowShown () {}
1093
-
1094
- fun onWindowHidden () {}
1095
- }
1096
-
1097
1115
companion object {
1098
- var self: TrimeInputMethodService ? = null
1116
+ /* * Delimiter regex to split language/locale tags. */
1117
+ private val DELIMITER_SPLITTER = """ [-_]""" .toRegex()
1118
+
1119
+ var instance: TrimeInputMethodService ? = null
1099
1120
1100
1121
@JvmStatic
1101
- fun getService (): TrimeInputMethodService = self ? : throw IllegalStateException (" Trime not initialized" )
1122
+ fun getService (): TrimeInputMethodService = instance ? : throw IllegalStateException (" TrimeInputMethodService is not initialized" )
1102
1123
1103
- fun getServiceOrNull (): TrimeInputMethodService ? = self
1124
+ fun getServiceOrNull (): TrimeInputMethodService ? = instance
1104
1125
1105
1126
private val syncBackgroundHandler =
1106
1127
Handler (
0 commit comments