@@ -7,9 +7,14 @@ package com.osfans.trime.ime.bar
77
88import android.content.Context
99import android.os.Build
10+ import android.util.Size
1011import android.view.View
12+ import android.view.ViewGroup
1113import android.view.inputmethod.EditorInfo
14+ import android.view.inputmethod.InlineSuggestion
15+ import android.view.inputmethod.InlineSuggestionsResponse
1216import android.widget.ViewAnimator
17+ import android.widget.inline.InlineContentView
1318import androidx.annotation.Keep
1419import androidx.annotation.RequiresApi
1520import androidx.lifecycle.lifecycleScope
@@ -23,10 +28,9 @@ import com.osfans.trime.data.theme.KeyActionManager
2328import com.osfans.trime.data.theme.Theme
2429import com.osfans.trime.ime.bar.ui.AlwaysUi
2530import com.osfans.trime.ime.bar.ui.CandidateUi
26- import com.osfans.trime.ime.bar.ui.InlineSuggestionUi
2731import com.osfans.trime.ime.bar.ui.TabUi
2832import com.osfans.trime.ime.broadcast.InputBroadcastReceiver
29- import com.osfans.trime.ime.candidates.CandidateModule
33+ import com.osfans.trime.ime.candidates.compact.CompactCandidateModule
3034import com.osfans.trime.ime.candidates.unrolled.window.FlexboxUnrolledCandidateWindow
3135import com.osfans.trime.ime.core.TrimeInputMethodService
3236import com.osfans.trime.ime.dependency.InputScope
@@ -39,13 +43,18 @@ import com.osfans.trime.ime.window.BoardWindowManager
3943import com.osfans.trime.ui.main.ClipEditActivity
4044import com.osfans.trime.util.AppUtils
4145import kotlinx.coroutines.Job
46+ import kotlinx.coroutines.async
47+ import kotlinx.coroutines.awaitAll
4248import kotlinx.coroutines.delay
4349import kotlinx.coroutines.launch
4450import me.tatarka.inject.annotations.Inject
4551import splitties.dimensions.dp
4652import splitties.views.dsl.core.add
4753import splitties.views.dsl.core.lParams
4854import splitties.views.dsl.core.matchParent
55+ import java.util.concurrent.Executor
56+ import kotlin.coroutines.resume
57+ import kotlin.coroutines.suspendCoroutine
4958
5059@InputScope
5160@Inject
@@ -55,7 +64,7 @@ class QuickBar(
5564 private val rime : RimeSession ,
5665 private val theme : Theme ,
5766 private val windowManager : BoardWindowManager ,
58- lazyCandidate : Lazy <CandidateModule >,
67+ lazyCandidate : Lazy <CompactCandidateModule >,
5968 lazyCommonKeyboardActionListener : Lazy <CommonKeyboardActionListener >,
6069) : InputBroadcastReceiver {
6170
@@ -76,6 +85,7 @@ class QuickBar(
7685 private var clipboardTimeoutJob: Job ? = null
7786
7887 private var isClipboardFresh: Boolean = false
88+ private var isInlineSuggestionPresent: Boolean = false
7989
8090 @Keep
8191 private val onClipboardUpdateListener = ClipboardHelper .OnClipboardUpdateListener {
@@ -107,6 +117,7 @@ class QuickBar(
107117 val newState =
108118 when {
109119 isClipboardFresh -> AlwaysUi .State .Clipboard
120+ isInlineSuggestionPresent -> AlwaysUi .State .InlineSuggestion
110121 else -> AlwaysUi .State .Toolbar
111122 }
112123 if (newState == alwaysUi.currentState) return
@@ -148,17 +159,13 @@ class QuickBar(
148159 }
149160
150161 private val candidateUi by lazy {
151- CandidateUi (context, candidate.compactCandidateModule. view).apply {
162+ CandidateUi (context, candidate.view).apply {
152163 unrollButton.apply {
153164 onSwipeListener = swipeDownHideKeyboardCallback
154165 }
155166 }
156167 }
157168
158- private val inlineSuggestionUi by lazy {
159- InlineSuggestionUi (context, candidate.suggestionCandidateModule.view)
160- }
161-
162169 private val tabUi by lazy {
163170 TabUi (context, theme)
164171 }
@@ -188,7 +195,7 @@ class QuickBar(
188195 private fun setUnrollButtonToAttach () {
189196 candidateUi.unrollButton.setOnClickListener { view ->
190197 windowManager.attachWindow(
191- FlexboxUnrolledCandidateWindow (context, service, rime, theme, this , windowManager, candidate.compactCandidateModule ),
198+ FlexboxUnrolledCandidateWindow (context, service, rime, theme, this , windowManager, candidate),
192199 )
193200 }
194201 candidateUi.unrollButton.setIcon(R .drawable.ic_baseline_expand_more_24)
@@ -241,7 +248,6 @@ class QuickBar(
241248 add(alwaysUi.root, lParams(matchParent, matchParent))
242249 add(candidateUi.root, lParams(matchParent, matchParent))
243250 add(tabUi.root, lParams(matchParent, matchParent))
244- add(inlineSuggestionUi.root, lParams(matchParent, matchParent))
245251
246252 evalAlwaysUiState()
247253 ClipboardHelper .addOnUpdateListener(onClipboardUpdateListener)
@@ -266,12 +272,59 @@ class QuickBar(
266272 barStateMachine.push(QuickBarStateMachine .TransitionEvent .WindowDetached )
267273 }
268274
275+ private val suggestionSize by lazy {
276+ Size (ViewGroup .LayoutParams .WRAP_CONTENT , context.dp(themedHeight))
277+ }
278+
279+ private val directExecutor by lazy {
280+ Executor { it.run () }
281+ }
282+
269283 @RequiresApi(Build .VERSION_CODES .R )
270- fun handleInlineSuggestions (isEmpty : Boolean ) {
271- barStateMachine.push(
272- QuickBarStateMachine .TransitionEvent .SuggestionUpdated ,
273- QuickBarStateMachine .BooleanKey .SuggestionEmpty to isEmpty,
274- )
284+ fun handleInlineSuggestions (response : InlineSuggestionsResponse ): Boolean {
285+ val suggestions = response.inlineSuggestions
286+ if (suggestions.isEmpty()) {
287+ isInlineSuggestionPresent = false
288+ return true
289+ }
290+ var pinned: InlineSuggestion ? = null
291+ val scrollable = mutableListOf<InlineSuggestion >()
292+ var extraPinnedCount = 0
293+ suggestions.forEach {
294+ if (it.info.isPinned) {
295+ if (pinned == null ) {
296+ pinned = it
297+ } else {
298+ scrollable.add(extraPinnedCount++ , it)
299+ }
300+ } else {
301+ scrollable.add(it)
302+ }
303+ }
304+ service.lifecycleScope.launch {
305+ alwaysUi.inlineSuggestionsUi.setPinnedView(
306+ pinned?.let { inflateInlineContentView(it) },
307+ )
308+ }
309+ service.lifecycleScope.launch {
310+ val views = scrollable.map { s ->
311+ service.lifecycleScope.async {
312+ inflateInlineContentView(s)
313+ }
314+ }.awaitAll()
315+ alwaysUi.inlineSuggestionsUi.setScrollableViews(views)
316+ }
317+ isInlineSuggestionPresent = true
318+ evalAlwaysUiState()
319+ return true
320+ }
321+
322+ @RequiresApi(Build .VERSION_CODES .R )
323+ private suspend fun inflateInlineContentView (suggestion : InlineSuggestion ): InlineContentView ? = suspendCoroutine { c ->
324+ // callback view might be null
325+ suggestion.inflate(context, suggestionSize, directExecutor) { v ->
326+ c.resume(v)
327+ }
275328 }
276329
277330 override fun onRimeOptionUpdated (value : RimeMessage .OptionMessage .Data ) {
0 commit comments