diff --git a/java/res/values/strings-uix.xml b/java/res/values/strings-uix.xml index aa146b7b89..38f8d3170d 100644 --- a/java/res/values/strings-uix.xml +++ b/java/res/values/strings-uix.xml @@ -501,6 +501,10 @@ Always use Western numerals Hide when USB keyboard is detected + Show toolbar when hardware keyboard is detected + When the keyboard is hidden because a physical keyboard is connected, keep the action/suggestions bar visible and add a button to show the touch keyboard. + Show touch keyboard + Hide touch keyboard Keyboard & Typing @@ -696,4 +700,4 @@ Default is %1$s Delete extra dictionary file? %1$s will be deleted - \ No newline at end of file + diff --git a/java/src/org/futo/inputmethod/latin/LatinIME.kt b/java/src/org/futo/inputmethod/latin/LatinIME.kt index 0632f74087..f6410d1a53 100644 --- a/java/src/org/futo/inputmethod/latin/LatinIME.kt +++ b/java/src/org/futo/inputmethod/latin/LatinIME.kt @@ -119,6 +119,11 @@ val HideKeyboardWhenHardKeyboardConnected = SettingsKey( false ) +val ShowToolbarWhenHardKeyboardConnected = SettingsKey( + booleanPreferencesKey("showToolbarWhenHardKeyboardConnected"), + false +) + private class UnlockedBroadcastReceiver(val onDeviceUnlocked: () -> Unit) : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == Intent.ACTION_USER_UNLOCKED) { @@ -423,6 +428,19 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont } } + launchJob { + combine( + getSettingFlow(HideKeyboardWhenHardKeyboardConnected), + getSettingFlow(ShowToolbarWhenHardKeyboardConnected) + ) { _, _ -> Unit }.collect { + withContext(Dispatchers.Main) { + uixManager.refreshHardwareToolbarMode() + updateInputViewShown() + onSizeMaybeUpdated() + } + } + } + launchJob { combine( getSettingFlow(HiddenKeysSetting), @@ -516,6 +534,7 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont updateNavigationBarVisibility() latinIMELegacy.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig) + uixManager.refreshHardwareToolbarMode() uixManager.updateLocaleOnCfgChanged() } @@ -609,6 +628,7 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) { super.onStartInput(attribute, restarting) latinIMELegacy.onStartInput(attribute, restarting) + uixManager.refreshHardwareToolbarMode() uixManager.inputStarted(attribute) //imeManager.onStartInput() // TODO: Is this call needed or not? } @@ -618,6 +638,7 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont onSizeMaybeUpdated() imeManager.onStartInput() latinIMELegacy.onStartInputView(info, restarting) + uixManager.refreshHardwareToolbarMode() lifecycleScope.launch { uixManager.showUpdateNoticeIfNeeded() } updateColorsIfDynamicChanged() uixManager.updateEmojiTranslationsIfNeeded() @@ -766,8 +787,17 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont return latinIMELegacy.onEvaluateInputViewShown() || super.onEvaluateInputViewShown() || !getSetting(HideKeyboardWhenHardKeyboardConnected) + || shouldUseHardwareKeyboardToolbarMode() } + fun hasHardwareKeyboardConnected(): Boolean = + Settings.readHasHardwareKeyboard(resources.configuration) + + fun shouldUseHardwareKeyboardToolbarMode(): Boolean = + hasHardwareKeyboardConnected() + && getSetting(HideKeyboardWhenHardKeyboardConnected) + && getSetting(ShowToolbarWhenHardKeyboardConnected) + override fun onEvaluateFullscreenMode(): Boolean { // TODO: Revisit fullscreen mode return false //latinIMELegacy.onEvaluateFullscreenMode(super.onEvaluateFullscreenMode()) @@ -941,4 +971,4 @@ class LatinIME : InputMethodServiceCompose(), LatinIMELegacy.SuggestionStripCont else -> KeyboardSizeSettingKind.Portrait } -} \ No newline at end of file +} diff --git a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt index 8ae519b348..f58fd2521e 100644 --- a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt +++ b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt @@ -825,6 +825,9 @@ fun ActionBar( onQuickClipDismiss: () -> Unit = {}, needToUseExpandableSuggestionUi: Boolean = false, loading: Boolean = false, + showTouchKeyboardToggle: Boolean = false, + touchKeyboardShown: Boolean = false, + onTouchKeyboardToggle: () -> Unit = {}, ) { val view = LocalView.current val context = LocalContext.current @@ -884,6 +887,12 @@ fun ActionBar( .fillMaxHeight()) { ActionItems(onActionActivated, onActionAltActivated) } + if(showTouchKeyboardToggle) { + TouchKeyboardToggleButton( + touchKeyboardShown = touchKeyboardShown, + onToggle = onTouchKeyboardToggle + ) + } } else { if (importantNotice != null) { ImportantNoticeView(importantNotice) @@ -922,6 +931,13 @@ fun ActionBar( Spacer(modifier = Modifier.weight(1.0f)) } + if(showTouchKeyboardToggle) { + TouchKeyboardToggleButton( + touchKeyboardShown = touchKeyboardShown, + onToggle = onTouchKeyboardToggle + ) + } + if(inlineSuggestions.isEmpty()) { PinnedActionItems(onActionActivated, onActionAltActivated) } @@ -934,6 +950,33 @@ fun ActionBar( } } +@Composable +private fun TouchKeyboardToggleButton( + touchKeyboardShown: Boolean, + onToggle: () -> Unit +) { + IconButton( + onClick = onToggle, + modifier = Modifier + .width(42.dp) + .fillMaxHeight(), + colors = IconButtonDefaults.iconButtonColors(contentColor = LocalKeyboardScheme.current.onBackground) + ) { + Icon( + painter = painterResource( + id = if (touchKeyboardShown) R.drawable.arrow_down else R.drawable.keyboard_regular + ), + contentDescription = stringResource( + if (touchKeyboardShown) { + R.string.keyboard_actionbar_hide_touch_keyboard + } else { + R.string.keyboard_actionbar_show_touch_keyboard + } + ) + ) + } +} + @Composable fun ActionWindowBar( windowTitleBar: @Composable RowScope.() -> Unit, @@ -1818,4 +1861,4 @@ fun PreviewActionBarLoadingDynamicDark() { @Preview fun PreviewExpandedActionBarDynamicDark() { PreviewExpandedActionBar(DynamicDarkTheme) -} \ No newline at end of file +} diff --git a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt index 6191a1e09c..87c6c185b6 100644 --- a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt +++ b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt @@ -603,6 +603,14 @@ class UixManager(private val latinIME: LatinIME) { private val keyboardManagerForAction = UixActionKeyboardManager(this, latinIME) private var mainKeyboardHidden = mutableStateOf(false) + private val hardwareToolbarMode = mutableStateOf(false) + private val forceTouchKeyboardForCurrentInput = mutableStateOf(false) + + val isHardwareToolbarModeEffective: Boolean + get() = hardwareToolbarMode.value && !forceTouchKeyboardForCurrentInput.value + + private val effectiveMainKeyboardHidden: Boolean + get() = mainKeyboardHidden.value || isHardwareToolbarModeEffective fun getCurrentLayoutName(): String = getPrimaryLayoutOverride(latinIME.currentInputEditorInfo) @@ -646,7 +654,35 @@ class UixManager(private val latinIME: LatinIME) { val touchableHeight: Int get() = measuredTouchableHeight - val isMainKeyboardHidden get() = mainKeyboardHidden.value + val isMainKeyboardHidden get() = effectiveMainKeyboardHidden + + fun refreshHardwareToolbarMode() { + val newValue = latinIME.shouldUseHardwareKeyboardToolbarMode() + if (!newValue) { + forceTouchKeyboardForCurrentInput.value = false + } + hardwareToolbarMode.value = newValue + } + + fun toggleTouchKeyboardForHardwareToolbarMode() { + if (!hardwareToolbarMode.value) return + forceTouchKeyboardForCurrentInput.value = !forceTouchKeyboardForCurrentInput.value + if (!effectiveMainKeyboardHidden) { + latinIME.onKeyboardShown() + } + } + + private fun expandActionWindowKeyboard() { + if (isHardwareToolbarModeEffective) { + forceTouchKeyboardForCurrentInput.value = true + } + + if (mainKeyboardHidden.value) { + toggleExpandAction(false) + } else if (!effectiveMainKeyboardHidden) { + latinIME.onKeyboardShown() + } + } private fun onActionActivatedInternal(rawAction: Action) { resizers.hideResizer() @@ -688,7 +724,11 @@ class UixManager(private val latinIME: LatinIME) { @Composable private fun MainKeyboardViewWithActionBar( - needToUseExpandableSuggestionUi: Boolean + needToUseExpandableSuggestionUi: Boolean, + forceActionBarShown: Boolean = false, + showTouchKeyboardToggle: Boolean = false, + touchKeyboardShown: Boolean = false, + onTouchKeyboardToggle: () -> Unit = {} ) { val view = LocalView.current @@ -706,7 +746,7 @@ class UixManager(private val latinIME: LatinIME) { if(!inlineStuffHiddenByTyping.value) inlineSuggestions.value else emptyList() } - if(actionBarShown.value || inlineSuggestions.isNotEmpty()) { + if(forceActionBarShown || actionBarShown.value || inlineSuggestions.isNotEmpty()) { ActionBar( suggestedWordsOrNull, suggestionStripListener, @@ -737,7 +777,10 @@ class UixManager(private val latinIME: LatinIME) { }, onQuickClipDismiss = { quickClipState.value = null }, needToUseExpandableSuggestionUi = needToUseExpandableSuggestionUi, - loading = latinIME.imeManager.isImeLoading() + loading = latinIME.imeManager.isImeLoading(), + showTouchKeyboardToggle = showTouchKeyboardToggle, + touchKeyboardShown = touchKeyboardShown, + onTouchKeyboardToggle = onTouchKeyboardToggle ) } } @@ -801,13 +844,14 @@ class UixManager(private val latinIME: LatinIME) { @Composable private fun ActionViewWithHeader(windowImpl: ActionWindow, needToUseExpandableSuggestionUi: Boolean) { - val heightDiv = if(mainKeyboardHidden.value) { + val touchKeyboardHidden = effectiveMainKeyboardHidden + val heightDiv = if(touchKeyboardHidden) { 1 } else { 1.5 } - val showingAboveKeyboard = !mainKeyboardHidden.value + val showingAboveKeyboard = !touchKeyboardHidden Column { Column( Modifier.background( @@ -818,11 +862,11 @@ class UixManager(private val latinIME: LatinIME) { } ) ) { - if (mainKeyboardHidden.value || isInputOverridden.value) { + if (touchKeyboardHidden || isInputOverridden.value) { ActionWindowBar( onBack = { closeActionWindow(true) }, canExpand = currWindowAction.value!!.canShowKeyboard, - onExpand = { toggleExpandAction() }, + onExpand = { expandActionWindowKeyboard() }, windowTitleBar = { windowImpl.WindowTitleBar(this) } ) } @@ -838,11 +882,11 @@ class UixManager(private val latinIME: LatinIME) { }) .safeKeyboardPadding() ) { - windowImpl.WindowContents(keyboardShown = !mainKeyboardHidden.value) + windowImpl.WindowContents(keyboardShown = !touchKeyboardHidden) } } - if((!mainKeyboardHidden.value && !latinIME.isInputConnectionOverridden) + if((!touchKeyboardHidden && !latinIME.isInputConnectionOverridden) || (latinIME.inputConnectionOverridenWithSuggestions)) { val suggestedWordsOrNull = if (shouldShowSuggestionStrip.value) { suggestedWords.value @@ -1281,7 +1325,7 @@ class UixManager(private val latinIME: LatinIME) { // TODO: Refactor how we handle expandable suggestions here to not be a mess val needToUseExpandableSuggestionUi = expandableSuggestionCfg.value.useExpandableUi && suggestedWords.value?.size()?.equals(0) != true - && mainKeyboardHidden.value == false + && !effectiveMainKeyboardHidden && (quickClipState.value == null || inlineStuffHiddenByTyping.value) && currentNotice.value == null && (inlineSuggestions.value.isEmpty() || inlineStuffHiddenByTyping.value) @@ -1292,7 +1336,11 @@ class UixManager(private val latinIME: LatinIME) { ) else -> MainKeyboardViewWithActionBar( - needToUseExpandableSuggestionUi + needToUseExpandableSuggestionUi, + forceActionBarShown = hardwareToolbarMode.value, + showTouchKeyboardToggle = hardwareToolbarMode.value, + touchKeyboardShown = !isHardwareToolbarModeEffective, + onTouchKeyboardToggle = { toggleTouchKeyboardForHardwareToolbarMode() } ) } @@ -1303,7 +1351,9 @@ class UixManager(private val latinIME: LatinIME) { val kbHeight = remember { mutableIntStateOf(latinIME.size.value!!.height) } val keyboardViewOffset = remember(needToUseExpandableSuggestionUi) { mutableIntStateOf(0) } Box(Modifier.let { - if(needToUseExpandableSuggestionUi) { + if(isHardwareToolbarModeEffective) { + it.height(0.dp) + } else if(needToUseExpandableSuggestionUi) { it.height( with(LocalDensity.current) { kbHeight.intValue.toDp() } + latinIME.sizingCalculator.calculateSuggestionBarHeightDp().dp @@ -1338,7 +1388,7 @@ class UixManager(private val latinIME: LatinIME) { } } .absoluteOffset { IntOffset(0, keyboardViewOffset.intValue) }, - hidden = mainKeyboardHidden.value) + hidden = effectiveMainKeyboardHidden) } if(latinIME.size.value !is FloatingKeyboardSize) { @@ -1608,6 +1658,8 @@ class UixManager(private val latinIME: LatinIME) { private val quickClipState: MutableState = mutableStateOf(null) fun dismissQuickClips() { quickClipState.value = null } fun inputStarted(editorInfo: EditorInfo?) { + forceTouchKeyboardForCurrentInput.value = false + refreshHardwareToolbarMode() try { checkIfDictInstalled() } catch(e: Exception) { @@ -1632,6 +1684,8 @@ class UixManager(private val latinIME: LatinIME) { } fun onInputFinishing() { + forceTouchKeyboardForCurrentInput.value = false + refreshHardwareToolbarMode() closeActionWindow() languageSwitcherDialog?.dismiss() isShowingActionEditor.value = false @@ -1715,5 +1769,3 @@ class UixManager(private val latinIME: LatinIME) { val extraTopTouchHeight: Int get() = if(floatingPreeditShown) floatingPreeditHeight.value else 0 } - - diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt index 0218653c24..7b399b7008 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt @@ -86,6 +86,7 @@ import org.futo.inputmethod.accessibility.AccessibilityUtils import org.futo.inputmethod.engine.IMESettingsMenu import org.futo.inputmethod.latin.HideKeyboardWhenHardKeyboardConnected import org.futo.inputmethod.latin.R +import org.futo.inputmethod.latin.ShowToolbarWhenHardKeyboardConnected import org.futo.inputmethod.latin.settings.LongPressKey import org.futo.inputmethod.latin.settings.LongPressKeyLayoutSetting import org.futo.inputmethod.latin.settings.Settings @@ -848,6 +849,12 @@ val KeyboardSettingsMenu = UserSettingsMenu( userSettingToggleDataStore( title = R.string.keyboard_settings_hide_when_hardware_keyboard_is_connected, setting = HideKeyboardWhenHardKeyboardConnected + ), + userSettingToggleDataStore( + title = R.string.keyboard_settings_show_toolbar_when_hardware_keyboard_is_connected, + subtitle = R.string.keyboard_settings_show_toolbar_when_hardware_keyboard_is_connected_subtitle, + setting = ShowToolbarWhenHardKeyboardConnected, + disabled = { !useDataStore(HideKeyboardWhenHardKeyboardConnected).value } ) ) ) @@ -1047,4 +1054,4 @@ fun KeyboardAndTypingScreen(navController: NavHostController = rememberNavContro BottomSpacer() } -} \ No newline at end of file +}