diff --git a/app/src/main/java/com/osfans/trime/data/prefs/AppPrefs.kt b/app/src/main/java/com/osfans/trime/data/prefs/AppPrefs.kt index a8120c0df2..01d95cbbf8 100644 --- a/app/src/main/java/com/osfans/trime/data/prefs/AppPrefs.kt +++ b/app/src/main/java/com/osfans/trime/data/prefs/AppPrefs.kt @@ -92,11 +92,13 @@ class AppPrefs( companion object { const val COMPOSING_TEXT_MODE = "composing_text_mode" const val ASCII_SWITCH_TIPS = "ascii_switch_tips" + const val INLINE_SUGGESTIONS = "inline_suggestions" const val PREFERRED_VOICE_INPUT = "preferred_voice_input" } val composingTextMode = enum(R.string.composing_text_mode, COMPOSING_TEXT_MODE, ComposingTextMode.DISABLE) val asciiSwitchTips = switch(R.string.ascii_switch_tips, ASCII_SWITCH_TIPS, true) + val inlineSuggestions = switch(R.string.inline_suggestions, INLINE_SUGGESTIONS, true) val preferredVoiceInput = list( R.string.preferred_voice_input, diff --git a/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt b/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt index 90dce217ca..f443285e82 100644 --- a/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt +++ b/app/src/main/java/com/osfans/trime/ime/bar/QuickBar.kt @@ -5,49 +5,56 @@ package com.osfans.trime.ime.bar -import android.content.ClipboardManager import android.content.Context import android.os.Build +import android.util.Size import android.view.View +import android.view.ViewGroup import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InlineSuggestion +import android.view.inputmethod.InlineSuggestionsResponse import android.widget.ViewAnimator +import android.widget.inline.InlineContentView import androidx.annotation.Keep import androidx.annotation.RequiresApi import androidx.lifecycle.lifecycleScope import com.osfans.trime.R -import com.osfans.trime.core.CandidateItem import com.osfans.trime.core.RimeMessage import com.osfans.trime.daemon.RimeSession +import com.osfans.trime.data.db.ClipboardHelper import com.osfans.trime.data.prefs.AppPrefs import com.osfans.trime.data.theme.ColorManager import com.osfans.trime.data.theme.KeyActionManager import com.osfans.trime.data.theme.Theme import com.osfans.trime.ime.bar.ui.AlwaysUi import com.osfans.trime.ime.bar.ui.CandidateUi -import com.osfans.trime.ime.bar.ui.ClipboardSuggestionUi -import com.osfans.trime.ime.bar.ui.InlineSuggestionUi import com.osfans.trime.ime.bar.ui.TabUi import com.osfans.trime.ime.broadcast.InputBroadcastReceiver -import com.osfans.trime.ime.candidates.CandidateModule +import com.osfans.trime.ime.candidates.compact.CompactCandidateModule import com.osfans.trime.ime.candidates.unrolled.window.FlexboxUnrolledCandidateWindow import com.osfans.trime.ime.core.TrimeInputMethodService import com.osfans.trime.ime.dependency.InputScope import com.osfans.trime.ime.keyboard.CommonKeyboardActionListener import com.osfans.trime.ime.keyboard.GestureFrame -import com.osfans.trime.ime.keyboard.InputFeedbackManager import com.osfans.trime.ime.keyboard.KeyboardWindow import com.osfans.trime.ime.switches.SwitchOptionWindow import com.osfans.trime.ime.window.BoardWindow import com.osfans.trime.ime.window.BoardWindowManager +import com.osfans.trime.ui.main.ClipEditActivity +import com.osfans.trime.util.AppUtils import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.tatarka.inject.annotations.Inject import splitties.dimensions.dp -import splitties.systemservices.clipboardManager import splitties.views.dsl.core.add import splitties.views.dsl.core.lParams import splitties.views.dsl.core.matchParent +import java.util.concurrent.Executor +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine @InputScope @Inject @@ -57,23 +64,16 @@ class QuickBar( private val rime: RimeSession, private val theme: Theme, private val windowManager: BoardWindowManager, - lazyCandidate: Lazy, + lazyCandidate: Lazy, lazyCommonKeyboardActionListener: Lazy, ) : InputBroadcastReceiver { - @Keep - private val clipboardListener = ClipboardManager.OnPrimaryClipChangedListener { - val clip = clipboardManager.primaryClip - val content = clip?.getItemAt(0)?.text?.toString()?.takeIf { it.isNotBlank() } - content?.let { handleClipboardContent(it) } - } - - private var clipboardTimeoutJob: Job? = null - private val candidate by lazyCandidate private val commonKeyboardActionListener by lazyCommonKeyboardActionListener + val themedHeight = theme.generalStyle.run { candidateViewHeight + commentHeight } + private val prefs = AppPrefs.defaultInstance() private val hideQuickBar by prefs.keyboard.hideQuickBar @@ -82,12 +82,42 @@ class QuickBar( private val clipboardSuggestionTimeout by prefs.clipboard.clipboardSuggestionTimeout - val themedHeight = - theme.generalStyle.candidateViewHeight + theme.generalStyle.commentHeight + private var clipboardTimeoutJob: Job? = null + + private var isClipboardFresh: Boolean = false + private var isInlineSuggestionPresent: Boolean = false + + @Keep + private val onClipboardUpdateListener = ClipboardHelper.OnClipboardUpdateListener { + if (!clipboardSuggestion) return@OnClipboardUpdateListener + service.lifecycleScope.launch { + if (it.text.isNullOrEmpty()) { + isClipboardFresh = false + } else { + alwaysUi.clipboardUi.text.text = it.text.take(42) + isClipboardFresh = true + launchClipboardTimeoutJob() + } + evalAlwaysUiState() + } + } + + private fun launchClipboardTimeoutJob() { + clipboardTimeoutJob?.cancel() + val timeout = clipboardSuggestionTimeout * 1000L + if (timeout < 0L) return + clipboardTimeoutJob = service.lifecycleScope.launch { + delay(timeout) + isClipboardFresh = false + clipboardTimeoutJob = null + } + } private fun evalAlwaysUiState() { val newState = when { + isClipboardFresh -> AlwaysUi.State.Clipboard + isInlineSuggestionPresent -> AlwaysUi.State.InlineSuggestion else -> AlwaysUi.State.Toolbar } if (newState == alwaysUi.currentState) return @@ -109,37 +139,37 @@ class QuickBar( setOnClickListener { service.requestHideSelf(0) } onSwipeListener = swipeDownHideKeyboardCallback } + clipboardUi.suggestionView.apply { + setOnClickListener { + val content = ClipboardHelper.lastBean?.text + content?.let { service.commitText(it) } + clipboardTimeoutJob?.cancel() + clipboardTimeoutJob = null + isClipboardFresh = false + evalAlwaysUiState() + } + setOnLongClickListener { + ClipboardHelper.lastBean?.let { + AppUtils.launchClipEdit(context, it.id, ClipEditActivity.FROM_CLIPBOARD) + } + true + } + } } } private val candidateUi by lazy { - CandidateUi(context, candidate.compactCandidateModule.view).apply { + CandidateUi(context, candidate.view).apply { unrollButton.apply { onSwipeListener = swipeDownHideKeyboardCallback } } } - private val inlineSuggestionUi by lazy { - InlineSuggestionUi(context, candidate.suggestionCandidateModule.view) - } - private val tabUi by lazy { TabUi(context, theme) } - private val clipboardSuggestionUi by lazy { - ClipboardSuggestionUi(context).apply { - root.setOnClickListener { - val content = text.text.toString() - if (content.isNotEmpty()) { - service.commitText(content) - handleClipboardContent(null) - } - } - } - } - private val barStateMachine = QuickBarStateMachine.new { switchUiByState(it) @@ -165,7 +195,7 @@ class QuickBar( private fun setUnrollButtonToAttach() { candidateUi.unrollButton.setOnClickListener { view -> windowManager.attachWindow( - FlexboxUnrolledCandidateWindow(context, service, rime, theme, this, windowManager, candidate.compactCandidateModule), + FlexboxUnrolledCandidateWindow(context, service, rime, theme, this, windowManager, candidate), ) } candidateUi.unrollButton.setIcon(R.drawable.ic_baseline_expand_more_24) @@ -218,12 +248,9 @@ class QuickBar( add(alwaysUi.root, lParams(matchParent, matchParent)) add(candidateUi.root, lParams(matchParent, matchParent)) add(tabUi.root, lParams(matchParent, matchParent)) - add(inlineSuggestionUi.root, lParams(matchParent, matchParent)) - add(clipboardSuggestionUi.root, lParams(matchParent, matchParent)) evalAlwaysUiState() - - clipboardManager.addPrimaryClipChangedListener(clipboardListener) + ClipboardHelper.addOnUpdateListener(onClipboardUpdateListener) } } @@ -245,30 +272,59 @@ class QuickBar( barStateMachine.push(QuickBarStateMachine.TransitionEvent.WindowDetached) } - @RequiresApi(Build.VERSION_CODES.R) - fun handleInlineSuggestions(isEmpty: Boolean) { - barStateMachine.push( - QuickBarStateMachine.TransitionEvent.SuggestionUpdated, - QuickBarStateMachine.BooleanKey.SuggestionEmpty to isEmpty, - ) + private val suggestionSize by lazy { + Size(ViewGroup.LayoutParams.WRAP_CONTENT, context.dp(themedHeight)) } - fun handleClipboardContent(content: String?) { - val isEmpty = content.isNullOrEmpty() || !clipboardSuggestion + private val directExecutor by lazy { + Executor { it.run() } + } - if (!isEmpty) { - clipboardSuggestionUi.text.text = content - clipboardTimeoutJob?.cancel() - clipboardTimeoutJob = service.lifecycleScope.launch { - delay(clipboardSuggestionTimeout * 1000L) - handleClipboardContent(null) + @RequiresApi(Build.VERSION_CODES.R) + fun handleInlineSuggestions(response: InlineSuggestionsResponse): Boolean { + val suggestions = response.inlineSuggestions + if (suggestions.isEmpty()) { + isInlineSuggestionPresent = false + return true + } + var pinned: InlineSuggestion? = null + val scrollable = mutableListOf() + var extraPinnedCount = 0 + suggestions.forEach { + if (it.info.isPinned) { + if (pinned == null) { + pinned = it + } else { + scrollable.add(extraPinnedCount++, it) + } + } else { + scrollable.add(it) } } + service.lifecycleScope.launch { + alwaysUi.inlineSuggestionsUi.setPinnedView( + pinned?.let { inflateInlineContentView(it) }, + ) + } + service.lifecycleScope.launch { + val views = scrollable.map { s -> + service.lifecycleScope.async { + inflateInlineContentView(s) + } + }.awaitAll() + alwaysUi.inlineSuggestionsUi.setScrollableViews(views) + } + isInlineSuggestionPresent = true + evalAlwaysUiState() + return true + } - barStateMachine.push( - QuickBarStateMachine.TransitionEvent.ClipboardUpdated, - QuickBarStateMachine.BooleanKey.ClipboardEmpty to isEmpty, - ) + @RequiresApi(Build.VERSION_CODES.R) + private suspend fun inflateInlineContentView(suggestion: InlineSuggestion): InlineContentView? = suspendCoroutine { c -> + // callback view might be null + suggestion.inflate(context, suggestionSize, directExecutor) { v -> + c.resume(v) + } } override fun onRimeOptionUpdated(value: RimeMessage.OptionMessage.Data) { diff --git a/app/src/main/java/com/osfans/trime/ime/bar/QuickBarStateMachine.kt b/app/src/main/java/com/osfans/trime/ime/bar/QuickBarStateMachine.kt index b5866c626d..f209ed3202 100644 --- a/app/src/main/java/com/osfans/trime/ime/bar/QuickBarStateMachine.kt +++ b/app/src/main/java/com/osfans/trime/ime/bar/QuickBarStateMachine.kt @@ -1,19 +1,13 @@ -// Copyright 2021 - 2023 Fcitx5 for Android Contributors -// Copyright 2021-2023 Fcitx5 for Android Contributors -// SPDX-FileCopyrightText: 2024 Rime community -// -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ package com.osfans.trime.ime.bar import com.osfans.trime.ime.bar.QuickBarStateMachine.BooleanKey.CandidateEmpty -import com.osfans.trime.ime.bar.QuickBarStateMachine.BooleanKey.ClipboardEmpty -import com.osfans.trime.ime.bar.QuickBarStateMachine.BooleanKey.SuggestionEmpty import com.osfans.trime.ime.bar.QuickBarStateMachine.State.Always import com.osfans.trime.ime.bar.QuickBarStateMachine.State.Candidate -import com.osfans.trime.ime.bar.QuickBarStateMachine.State.Clipboard -import com.osfans.trime.ime.bar.QuickBarStateMachine.State.Suggestion import com.osfans.trime.ime.bar.QuickBarStateMachine.State.Tab import com.osfans.trime.util.BuildTransitionEvent import com.osfans.trime.util.EventStateMachine @@ -24,14 +18,10 @@ object QuickBarStateMachine { Always, Candidate, Tab, - Suggestion, - Clipboard, } enum class BooleanKey : EventStateMachine.BooleanStateKey { CandidateEmpty, - SuggestionEmpty, - ClipboardEmpty, } enum class TransitionEvent( @@ -40,30 +30,16 @@ object QuickBarStateMachine { CandidatesUpdated({ from(Always) transitTo Candidate on (CandidateEmpty to false) from(Candidate) transitTo Always on (CandidateEmpty to true) - from(Suggestion) transitTo Candidate on (CandidateEmpty to false) - from(Clipboard) transitTo Candidate on (CandidateEmpty to false) }), BarBoardWindowAttached({ from(Always) transitTo Tab from(Candidate) transitTo Tab - from(Suggestion) transitTo Tab - from(Clipboard) transitTo Tab }), WindowDetached({ // candidate state has higher priority so here it goes first from(Tab) transitTo Candidate on (CandidateEmpty to false) - from(Tab) transitTo Suggestion on (SuggestionEmpty to false) - from(Tab) transitTo Clipboard on (ClipboardEmpty to false) from(Tab) transitTo Always }), - SuggestionUpdated({ - from(Always) transitTo Suggestion on (SuggestionEmpty to false) - from(Suggestion) transitTo Always on (SuggestionEmpty to true) - }), - ClipboardUpdated({ - from(Always) transitTo Clipboard on (ClipboardEmpty to false) - from(Clipboard) transitTo Always on (ClipboardEmpty to true) - }), } fun new(block: (State) -> Unit) = EventStateMachine( @@ -71,8 +47,6 @@ object QuickBarStateMachine { externalBooleanStates = mutableMapOf( CandidateEmpty to true, - SuggestionEmpty to true, - ClipboardEmpty to true, ), ).apply { onNewStateListener = block diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt index 4aeb5375d7..8b91d0696d 100644 --- a/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/AlwaysUi.kt @@ -1,6 +1,7 @@ -// SPDX-FileCopyrightText: 2015 - 2024 Rime community -// -// SPDX-License-Identifier: GPL-3.0-or-later +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ package com.osfans.trime.ime.bar.ui @@ -33,6 +34,8 @@ class AlwaysUi( ) : Ui { enum class State { Toolbar, + Clipboard, + InlineSuggestion, } var currentState = State.Toolbar @@ -57,9 +60,15 @@ class AlwaysUi( val buttonsUi = ButtonsBarUi(ctx, theme, onButtonClick) + val clipboardUi = ClipboardSuggestionUi(ctx) + + val inlineSuggestionsUi = InlineSuggestionsUi(ctx) + private val animator = ViewAnimator(ctx).apply { add(buttonsUi.root, lParams(matchParent, matchParent)) + add(clipboardUi.root, lParams(matchParent, matchParent)) + add(inlineSuggestionsUi.root, lParams(matchParent, matchParent)) } override val root: ConstraintLayout = constraintLayout { @@ -106,6 +115,8 @@ class AlwaysUi( Timber.d("Switch always ui to $state") when (state) { State.Toolbar -> animator.displayedChild = 0 + State.Clipboard -> animator.displayedChild = 1 + State.InlineSuggestion -> animator.displayedChild = 2 } currentState = state } diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/ClipboardSuggestionUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/ClipboardSuggestionUi.kt index c555ab30c1..002d38d8ac 100644 --- a/app/src/main/java/com/osfans/trime/ime/bar/ui/ClipboardSuggestionUi.kt +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/ClipboardSuggestionUi.kt @@ -1,6 +1,6 @@ /* - * SPDX-License-Identifier: LGPL-2.1-or-later - * SPDX-FileCopyrightText: Copyright 2021-2023 Fcitx5 for Android Contributors + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later */ package com.osfans.trime.ime.bar.ui @@ -8,6 +8,8 @@ import android.content.Context import android.text.TextUtils import com.osfans.trime.R import com.osfans.trime.data.theme.ColorManager +import com.osfans.trime.ime.keyboard.GestureFrame +import com.osfans.trime.util.rippleDrawable import splitties.dimensions.dp import splitties.resources.drawable import splitties.views.dsl.constraintlayout.after @@ -69,10 +71,15 @@ class ClipboardSuggestionUi( ) } + val suggestionView = GestureFrame(ctx).apply { + add(layout, lParams(wrapContent, matchParent)) + background = rippleDrawable(ColorManager.getColor("hilited_candidate_back_color")) + } + override val root = constraintLayout { add( - layout, + suggestionView, lParams(wrapContent, matchConstraints) { centerInParent() verticalMargin = dp(4) diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/InlineSuggestionUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/InlineSuggestionUi.kt deleted file mode 100644 index 3fd60b09d4..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/bar/ui/InlineSuggestionUi.kt +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Rime community -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package com.osfans.trime.ime.bar.ui - -import android.content.Context -import android.view.View -import splitties.views.dsl.constraintlayout.centerVertically -import splitties.views.dsl.constraintlayout.constraintLayout -import splitties.views.dsl.constraintlayout.endOfParent -import splitties.views.dsl.constraintlayout.lParams -import splitties.views.dsl.constraintlayout.startOfParent -import splitties.views.dsl.core.Ui -import splitties.views.dsl.core.add -import splitties.views.dsl.core.wrapContent - -class InlineSuggestionUi( - override val ctx: Context, - private val compatView: View, -) : Ui { - override val root = - ctx.constraintLayout { - add( - compatView, - lParams(wrapContent, wrapContent) { - centerVertically() - startOfParent() - endOfParent() - }, - ) - } -} diff --git a/app/src/main/java/com/osfans/trime/ime/bar/ui/InlineSuggestionsUi.kt b/app/src/main/java/com/osfans/trime/ime/bar/ui/InlineSuggestionsUi.kt new file mode 100644 index 0000000000..fbc7c0890d --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ime/bar/ui/InlineSuggestionsUi.kt @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.osfans.trime.ime.bar.ui + +import android.content.Context +import android.os.Build +import android.view.SurfaceControl +import android.view.SurfaceView +import android.widget.FrameLayout +import android.widget.HorizontalScrollView +import android.widget.inline.InlineContentView +import androidx.annotation.RequiresApi +import androidx.core.view.updateLayoutParams +import com.google.android.flexbox.FlexWrap +import com.google.android.flexbox.FlexboxLayout +import com.google.android.flexbox.JustifyContent +import splitties.dimensions.dp +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.centerOn +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.frameLayout +import splitties.views.dsl.core.horizontalMargin +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.view +import splitties.views.dsl.core.wrapContent + +class InlineSuggestionsUi(override val ctx: Context) : Ui { + private val scrollView = ctx.view(::HorizontalScrollView) { + isFillViewport = true + scrollBarSize = dp(1) + } + + private val scrollSurfaceView = ctx.view(::SurfaceView) { + setZOrderOnTop(true) + } + + private val scrollableContentViews = mutableListOf() + + private val pinnedView = frameLayout { } + + private var pinnedContentView: InlineContentView? = null + + override val root = constraintLayout { + add( + scrollView, + lParams(matchConstraints, matchParent) { + startOfParent() + before(pinnedView) + centerVertically() + }, + ) + add( + scrollSurfaceView, + lParams(matchConstraints, matchParent) { + centerOn(scrollView) + }, + ) + add( + pinnedView, + lParams(wrapContent, matchParent) { + endOfParent() + centerVertically() + }, + ) + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun clearScrollView() { + scrollView.scrollTo(0, 0) + scrollView.removeAllViews() + scrollableContentViews.forEach { v -> + v.surfaceControl?.let { sc -> + SurfaceControl.Transaction().reparent(sc, null).apply() + } + } + scrollableContentViews.clear() + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun clearPinnedView() { + pinnedView.removeAllViews() + pinnedContentView = null + } + + @RequiresApi(Build.VERSION_CODES.R) + fun clear() { + clearScrollView() + clearPinnedView() + } + + @RequiresApi(Build.VERSION_CODES.R) + fun setPinnedView(view: InlineContentView?) { + pinnedView.removeAllViews() + pinnedContentView = view?.also { + pinnedView.addView(it) + it.updateLayoutParams { + horizontalMargin = ctx.dp(10) + } + } + } + + @RequiresApi(Build.VERSION_CODES.R) + fun setScrollableViews(views: List) { + val flexbox = view(::FlexboxLayout) { + flexWrap = FlexWrap.NOWRAP + justifyContent = JustifyContent.CENTER + } + val parentSurfaceControl = scrollSurfaceView.surfaceControl + views.forEach { + if (it == null) return@forEach + scrollableContentViews.add(it) + it.setSurfaceControlCallback(object : InlineContentView.SurfaceControlCallback { + override fun onCreated(surfaceControl: SurfaceControl) { + SurfaceControl.Transaction() + .reparent(surfaceControl, parentSurfaceControl) + .apply() + } + + override fun onDestroyed(surfaceControl: SurfaceControl) {} + }) + flexbox.addView(it) + it.updateLayoutParams { + flexShrink = 0f + } + } + scrollView.apply { + scrollTo(0, 0) + removeAllViews() + add(flexbox, lParams(wrapContent, matchParent)) + } + } +} diff --git a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt index 1d83d0a97e..6642c674a0 100644 --- a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt +++ b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcastReceiver.kt @@ -6,8 +6,6 @@ package com.osfans.trime.ime.broadcast import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InlineSuggestion -import com.osfans.trime.core.CandidateItem import com.osfans.trime.core.RimeMessage import com.osfans.trime.core.RimeProto import com.osfans.trime.core.SchemaItem @@ -38,6 +36,4 @@ interface InputBroadcastReceiver { fun onWindowDetached(window: BoardWindow) {} fun onEnterKeyLabelUpdate(label: String) {} - - fun onInlineSuggestions(suggestions: List) {} } diff --git a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt index a63a66ca82..71a65cb5de 100644 --- a/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt +++ b/app/src/main/java/com/osfans/trime/ime/broadcast/InputBroadcaster.kt @@ -85,8 +85,4 @@ class InputBroadcaster : InputBroadcastReceiver { override fun onEnterKeyLabelUpdate(label: String) { receivers.forEach { it.onEnterKeyLabelUpdate(label) } } - - override fun onInlineSuggestions(suggestions: List) { - receivers.forEach { it.onInlineSuggestions(suggestions) } - } } diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/CandidateModule.kt b/app/src/main/java/com/osfans/trime/ime/candidates/CandidateModule.kt deleted file mode 100644 index c0eea9f127..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/candidates/CandidateModule.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 - 2025 Rime community - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.osfans.trime.ime.candidates - -import android.content.Context -import com.osfans.trime.daemon.RimeSession -import com.osfans.trime.data.theme.Theme -import com.osfans.trime.ime.bar.QuickBar -import com.osfans.trime.ime.candidates.compact.CompactCandidateModule -import com.osfans.trime.ime.candidates.suggestion.SuggestionCandidateModule -import com.osfans.trime.ime.core.TrimeInputMethodService -import com.osfans.trime.ime.dependency.InputScope -import me.tatarka.inject.annotations.Inject - -@InputScope -@Inject -class CandidateModule( - val context: Context, - val service: TrimeInputMethodService, - val rime: RimeSession, - val theme: Theme, - val bar: QuickBar, -) { - val compactCandidateModule = CompactCandidateModule(context, service, rime, theme, bar) - val suggestionCandidateModule = SuggestionCandidateModule(context, service, rime, theme, bar) -} diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/compact/CompactCandidateModule.kt b/app/src/main/java/com/osfans/trime/ime/candidates/compact/CompactCandidateModule.kt index a476e314f6..282dbed962 100644 --- a/app/src/main/java/com/osfans/trime/ime/candidates/compact/CompactCandidateModule.kt +++ b/app/src/main/java/com/osfans/trime/ime/candidates/compact/CompactCandidateModule.kt @@ -17,7 +17,6 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager import com.osfans.trime.R -import com.osfans.trime.core.CandidateItem import com.osfans.trime.core.RimeMessage import com.osfans.trime.daemon.RimeSession import com.osfans.trime.daemon.launchOnReady @@ -28,16 +27,19 @@ import com.osfans.trime.ime.bar.UnrollButtonStateMachine import com.osfans.trime.ime.broadcast.InputBroadcastReceiver import com.osfans.trime.ime.candidates.unrolled.decoration.FlexboxVerticalDecoration import com.osfans.trime.ime.core.TrimeInputMethodService +import com.osfans.trime.ime.dependency.InputScope import com.osfans.trime.ime.keyboard.InputFeedbackManager import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import me.tatarka.inject.annotations.Inject import splitties.dimensions.dp import splitties.views.dsl.recyclerview.recyclerView import kotlin.math.max +@InputScope +@Inject class CompactCandidateModule( val context: Context, val service: TrimeInputMethodService, diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/InlineSuggestionHelper.kt b/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/InlineSuggestionHelper.kt deleted file mode 100644 index ad950c8bfe..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/InlineSuggestionHelper.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 - 2025 Rime community - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.osfans.trime.ime.candidates.suggestion - -import android.annotation.SuppressLint -import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.drawable.Icon -import android.os.Build -import android.util.Size -import android.view.inputmethod.InlineSuggestionsRequest -import android.widget.inline.InlinePresentationSpec -import androidx.annotation.RequiresApi -import androidx.autofill.inline.UiVersions -import androidx.autofill.inline.common.ImageViewStyle -import androidx.autofill.inline.common.TextViewStyle -import androidx.autofill.inline.common.ViewStyle -import androidx.autofill.inline.v1.InlineSuggestionUi -import androidx.core.graphics.ColorUtils -import com.osfans.trime.R -import com.osfans.trime.data.theme.ColorManager -import splitties.dimensions.dp - -object InlineSuggestionHelper { - @SuppressLint("RestrictedApi") - @RequiresApi(Build.VERSION_CODES.R) - fun createRequest(ctx: Context): InlineSuggestionsRequest { - val firstTextColor = ColorManager.getColor("candidate_text_color") - val backColor = ColorManager.getColor("hilited_back_color") - val chipBg = - Icon.createWithResource(ctx, R.drawable.bg_inline_suggestion).apply { - setTint(ColorUtils.blendARGB(backColor, firstTextColor, 0.2f)) - } - val style = - InlineSuggestionUi - .newStyleBuilder() - .setSingleIconChipStyle( - ViewStyle - .Builder() - .setBackgroundColor(Color.TRANSPARENT) - .setPadding(0, 0, 0, 0) - .build(), - ).setChipStyle( - ViewStyle - .Builder() - .setBackground(chipBg) - .build(), - ).setTitleStyle( - TextViewStyle - .Builder() - .setLayoutMargin(ctx.dp(4), 0, ctx.dp(4), 0) - .setTextColor(firstTextColor) - .setTextSize(14f) - .build(), - ).setSubtitleStyle( - TextViewStyle - .Builder() - .setTextColor( - ColorUtils.blendARGB(firstTextColor, backColor, 0.3f), - ).setTextSize(12f) - .build(), - ).setStartIconStyle( - ImageViewStyle - .Builder() - .setTintList(ColorStateList.valueOf(firstTextColor)) - .build(), - ).setEndIconStyle( - ImageViewStyle - .Builder() - .setTintList(ColorStateList.valueOf(firstTextColor)) - .build(), - ).build() - val styleBundle = - UiVersions - .newStylesBuilder() - .addStyle(style) - .build() - val spec = - InlinePresentationSpec - .Builder(Size(0, 0), Size(Int.MAX_VALUE, Int.MAX_VALUE)) - .setStyle(styleBundle) - .build() - return InlineSuggestionsRequest - .Builder(listOf(spec)) - .setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED) - .build() - } -} diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionCandidateModule.kt b/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionCandidateModule.kt deleted file mode 100644 index b86381a34c..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionCandidateModule.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 - 2024 Rime community - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.osfans.trime.ime.candidates.suggestion - -import android.content.Context -import android.os.Build -import android.util.Size -import android.view.ViewGroup -import android.view.inputmethod.InlineSuggestion -import android.widget.inline.InlineContentView -import androidx.annotation.RequiresApi -import androidx.lifecycle.lifecycleScope -import com.osfans.trime.R -import com.osfans.trime.daemon.RimeSession -import com.osfans.trime.data.theme.Theme -import com.osfans.trime.ime.bar.QuickBar -import com.osfans.trime.ime.broadcast.InputBroadcastReceiver -import com.osfans.trime.ime.core.TrimeInputMethodService -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.launch -import splitties.dimensions.dp -import splitties.views.dsl.recyclerview.recyclerView -import splitties.views.recyclerview.horizontalLayoutManager -import java.util.concurrent.Executor -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine - -class SuggestionCandidateModule( - val context: Context, - val service: TrimeInputMethodService, - val rime: RimeSession, - val theme: Theme, - val bar: QuickBar, -) : InputBroadcastReceiver { - private val adapter by lazy { - SuggestionViewAdapter(theme) - } - - val view by lazy { - context.recyclerView(R.id.suggestion_view) { - adapter = this@SuggestionCandidateModule.adapter - layoutManager = horizontalLayoutManager() - isVerticalScrollBarEnabled = false - isHorizontalScrollBarEnabled = false - } - } - - private val suggestionSize by lazy { - Size(ViewGroup.LayoutParams.WRAP_CONTENT, context.dp(HEIGHT)) - } - - private val directExecutor by lazy { - Executor { it.run() } - } - - @RequiresApi(Build.VERSION_CODES.R) - override fun onInlineSuggestions(suggestions: List) { - service.lifecycleScope.launch { - val items = - suggestions - .map { s -> - service.lifecycleScope.async { - SuggestionViewItem(inflateInlineContentView(s)) - } - }.awaitAll() - adapter.submitList(items) - } - } - - @RequiresApi(Build.VERSION_CODES.R) - private suspend fun inflateInlineContentView(suggestion: InlineSuggestion): InlineContentView? = suspendCoroutine { c -> - // callback view might be null - suggestion.inflate(context, suggestionSize, directExecutor) { v -> - c.resume(v) - } - } - - companion object { - const val HEIGHT = 40 - } -} diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionItemUi.kt b/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionItemUi.kt deleted file mode 100644 index 35ad5a489e..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionItemUi.kt +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Rime community -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package com.osfans.trime.ime.candidates.suggestion - -import android.content.Context -import android.os.Build -import android.view.View -import androidx.annotation.RequiresApi -import com.osfans.trime.data.theme.ColorManager -import com.osfans.trime.util.pressHighlightDrawable -import splitties.views.dsl.constraintlayout.constraintLayout -import splitties.views.dsl.core.Ui - -class SuggestionItemUi( - override val ctx: Context, -) : Ui { - override val root = - constraintLayout { - background = pressHighlightDrawable(ColorManager.getColor("hilited_candidate_back_color")) - } - - @RequiresApi(Build.VERSION_CODES.R) - fun addView(view: View) { - root.removeAllViews() - root.addView(view) - } -} diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionViewAdapter.kt b/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionViewAdapter.kt deleted file mode 100644 index e6eb5d6061..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionViewAdapter.kt +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Rime community -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package com.osfans.trime.ime.candidates.suggestion - -import android.content.Context -import android.os.Build -import android.view.ViewGroup -import androidx.annotation.RequiresApi -import androidx.recyclerview.widget.RecyclerView -import com.chad.library.adapter4.BaseQuickAdapter -import com.osfans.trime.data.theme.Theme -import splitties.dimensions.dp -import splitties.views.dsl.core.matchParent -import splitties.views.dsl.core.wrapContent -import splitties.views.setPaddingDp - -class SuggestionViewAdapter( - private val theme: Theme, -) : BaseQuickAdapter() { - inner class ViewHolder( - val ui: SuggestionItemUi, - ) : RecyclerView.ViewHolder(ui.root) - - override fun onCreateViewHolder( - context: Context, - parent: ViewGroup, - viewType: Int, - ): ViewHolder { - val ui = SuggestionItemUi(context) - ui.root.apply { - minimumWidth = dp(40) - val size = theme.generalStyle.candidatePadding - setPaddingDp(size, 0, size, 0) - layoutParams = RecyclerView.LayoutParams(wrapContent, matchParent) - } - return ViewHolder(ui) - } - - @RequiresApi(Build.VERSION_CODES.R) - override fun onBindViewHolder( - holder: ViewHolder, - position: Int, - item: SuggestionViewItem?, - ) { - item?.view ?: return - holder.ui.addView(item.view) - } -} diff --git a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionViewItem.kt b/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionViewItem.kt deleted file mode 100644 index 22f0e0770e..0000000000 --- a/app/src/main/java/com/osfans/trime/ime/candidates/suggestion/SuggestionViewItem.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 - 2025 Rime community - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.osfans.trime.ime.candidates.suggestion - -import android.widget.inline.InlineContentView - -data class SuggestionViewItem( - val view: InlineContentView?, -) diff --git a/app/src/main/java/com/osfans/trime/ime/core/InlineSuggestions.kt b/app/src/main/java/com/osfans/trime/ime/core/InlineSuggestions.kt new file mode 100644 index 0000000000..c27b58f521 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ime/core/InlineSuggestions.kt @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.osfans.trime.ime.core + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.Icon +import android.os.Build +import android.util.Size +import android.view.inputmethod.InlineSuggestionsRequest +import android.widget.inline.InlinePresentationSpec +import androidx.annotation.RequiresApi +import androidx.autofill.inline.UiVersions +import androidx.autofill.inline.common.ImageViewStyle +import androidx.autofill.inline.common.TextViewStyle +import androidx.autofill.inline.common.ViewStyle +import androidx.autofill.inline.v1.InlineSuggestionUi +import com.osfans.trime.R +import com.osfans.trime.data.theme.ColorManager +import com.osfans.trime.util.ColorUtils +import splitties.dimensions.dp + +object InlineSuggestions { + @SuppressLint("RestrictedApi") + @RequiresApi(Build.VERSION_CODES.R) + fun createRequest(ctx: Context): InlineSuggestionsRequest { + val textColor = ColorManager.getColor("candidate_text_color") + val altTextColor = ColorManager.getColor("comment_text_color") + val isDark = ColorUtils.isContrastedDark(ColorManager.getColor("back_color")) + val chipDrawable = if (isDark) { + R.drawable.bg_inline_suggestion_dark + } else { + R.drawable.bg_inline_suggestion_light + } + val chipBg = Icon.createWithResource(ctx, chipDrawable).apply { + setTint(textColor) + } + val style = InlineSuggestionUi.newStyleBuilder() + .setSingleIconChipStyle( + ViewStyle.Builder() + .setBackgroundColor(Color.TRANSPARENT) + .setPadding(0, 0, 0, 0) + .build(), + ).setChipStyle( + ViewStyle.Builder() + .setBackground(chipBg) + .setPadding(ctx.dp(10), 0, ctx.dp(10), 0) + .build(), + ).setTitleStyle( + TextViewStyle.Builder() + .setLayoutMargin(ctx.dp(4), 0, ctx.dp(4), 0) + .setTextColor(textColor) + .setTextSize(14f) + .build(), + ).setSubtitleStyle( + TextViewStyle.Builder() + .setTextColor(altTextColor) + .setTextSize(12f) + .build(), + ).setStartIconStyle( + ImageViewStyle.Builder() + .setTintList(ColorStateList.valueOf(altTextColor)) + .build(), + ).setEndIconStyle( + ImageViewStyle.Builder() + .setTintList(ColorStateList.valueOf(altTextColor)) + .build(), + ).build() + val styleBundle = UiVersions.newStylesBuilder() + .addStyle(style) + .build() + val spec = InlinePresentationSpec + .Builder(Size(0, 0), Size(Int.MAX_VALUE, Int.MAX_VALUE)) + .setStyle(styleBundle) + .build() + return InlineSuggestionsRequest.Builder(listOf(spec)) + .setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED) + .build() + } +} diff --git a/app/src/main/java/com/osfans/trime/ime/core/InputView.kt b/app/src/main/java/com/osfans/trime/ime/core/InputView.kt index 3b3461d381..9c448267df 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/InputView.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/InputView.kt @@ -26,7 +26,6 @@ import com.osfans.trime.data.theme.Theme import com.osfans.trime.ime.bar.QuickBar import com.osfans.trime.ime.candidates.compact.CompactCandidateModule import com.osfans.trime.ime.candidates.popup.PopupCandidatesMode -import com.osfans.trime.ime.candidates.suggestion.SuggestionCandidateModule import com.osfans.trime.ime.composition.PreeditModule import com.osfans.trime.ime.dependency.InputComponent import com.osfans.trime.ime.dependency.create @@ -98,8 +97,7 @@ class InputView( private val preedit: PreeditModule = inputComponent.preedit private val keyboardWindow: KeyboardWindow = inputComponent.keyboardWindow private val liquidWindow: LiquidWindow = inputComponent.liquidWindow - private val compactCandidate: CompactCandidateModule = inputComponent.candidate.compactCandidateModule - private val suggestionCandidate: SuggestionCandidateModule = inputComponent.candidate.suggestionCandidateModule + private val compactCandidate: CompactCandidateModule = inputComponent.candidate private val popup: PopupComponent = inputComponent.popup private fun addBroadcastReceivers() { @@ -108,7 +106,6 @@ class InputView( broadcaster.addReceiver(keyboardWindow) broadcaster.addReceiver(liquidWindow) broadcaster.addReceiver(compactCandidate) - broadcaster.addReceiver(suggestionCandidate) } private val composingTextMode by AppPrefs.defaultInstance().general.composingTextMode @@ -334,12 +331,7 @@ class InputView( } @RequiresApi(Build.VERSION_CODES.R) - fun handleInlineSuggestions(response: InlineSuggestionsResponse): Boolean { - val suggestions = response.inlineSuggestions - broadcaster.onInlineSuggestions(suggestions) - quickBar.handleInlineSuggestions(suggestions.isEmpty()) - return true - } + fun handleInlineSuggestions(response: InlineSuggestionsResponse): Boolean = quickBar.handleInlineSuggestions(response) override fun onDetachedFromWindow() { ViewCompat.setOnApplyWindowInsetsListener(this, null) 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 89eae0990f..77552b5576 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 @@ -49,7 +49,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.candidates.suggestion.InlineSuggestionHelper import com.osfans.trime.ime.composition.CandidatesView import com.osfans.trime.ime.keyboard.InputFeedbackManager import com.osfans.trime.receiver.RimeIntentReceiver @@ -482,10 +481,12 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { } } + private val inlineSuggestions by prefs.general.inlineSuggestions + @RequiresApi(Build.VERSION_CODES.R) override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? { - if (!inputDeviceManager.isVirtualKeyboard) return null - return InlineSuggestionHelper.createRequest(this) + if (!inlineSuggestions || !inputDeviceManager.isVirtualKeyboard) return null + return InlineSuggestions.createRequest(this) } @SuppressLint("NewApi") diff --git a/app/src/main/java/com/osfans/trime/ime/dependency/InputComponent.kt b/app/src/main/java/com/osfans/trime/ime/dependency/InputComponent.kt index 67e13e7dd2..382d09a727 100644 --- a/app/src/main/java/com/osfans/trime/ime/dependency/InputComponent.kt +++ b/app/src/main/java/com/osfans/trime/ime/dependency/InputComponent.kt @@ -10,7 +10,7 @@ import com.osfans.trime.data.theme.Theme import com.osfans.trime.ime.bar.QuickBar import com.osfans.trime.ime.broadcast.EnterKeyLabelModule import com.osfans.trime.ime.broadcast.InputBroadcaster -import com.osfans.trime.ime.candidates.CandidateModule +import com.osfans.trime.ime.candidates.compact.CompactCandidateModule import com.osfans.trime.ime.composition.PreeditModule import com.osfans.trime.ime.core.InputView import com.osfans.trime.ime.core.TrimeInputMethodService @@ -40,5 +40,5 @@ abstract class InputComponent( abstract val keyboardWindow: KeyboardWindow abstract val commonKeyboardActionListener: CommonKeyboardActionListener abstract val liquidWindow: LiquidWindow - abstract val candidate: CandidateModule + abstract val candidate: CompactCandidateModule } diff --git a/app/src/main/java/com/osfans/trime/ime/keyboard/CommonKeyboardActionListener.kt b/app/src/main/java/com/osfans/trime/ime/keyboard/CommonKeyboardActionListener.kt index d14d41e0e8..7a2ec9ad2c 100644 --- a/app/src/main/java/com/osfans/trime/ime/keyboard/CommonKeyboardActionListener.kt +++ b/app/src/main/java/com/osfans/trime/ime/keyboard/CommonKeyboardActionListener.kt @@ -178,8 +178,6 @@ class CommonKeyboardActionListener( else -> handleDefaultKeyAction(action) } } - - quickBar.handleClipboardContent(null) } private fun handleSwitchCharset(action: KeyAction) { diff --git a/app/src/main/res/drawable/bg_inline_suggestion.xml b/app/src/main/res/drawable/bg_inline_suggestion_dark.xml similarity index 85% rename from app/src/main/res/drawable/bg_inline_suggestion.xml rename to app/src/main/res/drawable/bg_inline_suggestion_dark.xml index 151468b321..d1d916c9a8 100644 --- a/app/src/main/res/drawable/bg_inline_suggestion.xml +++ b/app/src/main/res/drawable/bg_inline_suggestion_dark.xml @@ -13,10 +13,10 @@ android:top="4dp"> - + + android:color="#4DFFFFFF" /> diff --git a/app/src/main/res/drawable/bg_inline_suggestion_light.xml b/app/src/main/res/drawable/bg_inline_suggestion_light.xml new file mode 100644 index 0000000000..67e40103cf --- /dev/null +++ b/app/src/main/res/drawable/bg_inline_suggestion_light.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index bfcdd48442..8ef33edaa0 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -208,4 +208,5 @@ 始终 首选语音输入法 未安装语音输入 + 显示内嵌自动填充建议 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index eb38ae30b5..49239e94f5 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -209,4 +209,5 @@ 始終 首選語音輸入 尚未安裝語音輸入 + 顯示內嵌自動填充建議 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c3ab95e91c..2c5b857c20 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -209,4 +209,5 @@ Always Preferred voice input No voice input installed + Show inline autofill suggestions