diff --git a/java/src/org/futo/inputmethod/latin/uix/Action.kt b/java/src/org/futo/inputmethod/latin/uix/Action.kt index aa3959a48..9d110e4f7 100644 --- a/java/src/org/futo/inputmethod/latin/uix/Action.kt +++ b/java/src/org/futo/inputmethod/latin/uix/Action.kt @@ -6,7 +6,12 @@ import android.view.View import android.view.inputmethod.InputConnection import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.lifecycle.LifecycleCoroutineScope import org.futo.inputmethod.latin.uix.theme.ThemeOption import java.util.Locale @@ -17,6 +22,11 @@ interface ActionInputTransaction { fun cancel() } +data class DialogRequestItem( + val option: String, + val onClick: () -> Unit +) + interface KeyboardManagerForAction { fun getContext(): Context fun getLifecycleScope(): LifecycleCoroutineScope @@ -48,6 +58,8 @@ interface KeyboardManagerForAction { fun overrideInputConnection(inputConnection: InputConnection) fun unsetInputConnection() + + fun requestDialog(text: String, options: List, onCancel: () -> Unit) } interface ActionWindow { @@ -57,6 +69,14 @@ interface ActionWindow { @Composable fun WindowContents(keyboardShown: Boolean) + @Composable + fun WindowTitleBar(rowScope: RowScope) { + with(rowScope) { + Text(windowName(), modifier = Modifier.align(Alignment.CenterVertically)) + Spacer(modifier = Modifier.weight(1.0f)) + } + } + fun close() } diff --git a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt index 7b64fac7a..ab7e33fff 100644 --- a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt +++ b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt @@ -30,9 +30,9 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme @@ -61,7 +61,6 @@ import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback @@ -674,7 +673,7 @@ fun ActionBar( @Composable fun ActionWindowBar( - windowName: String, + windowTitleBar: @Composable RowScope.() -> Unit, canExpand: Boolean, onBack: () -> Unit, onExpand: () -> Unit @@ -693,13 +692,9 @@ fun ActionWindowBar( ) } - Text( - windowName, - style = Typography.titleMedium, - modifier = Modifier.align(CenterVertically) - ) - - Spacer(modifier = Modifier.weight(1.0f)) + CompositionLocalProvider(LocalTextStyle provides Typography.titleMedium) { + windowTitleBar() + } if(canExpand) { IconButton(onClick = onExpand) { diff --git a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt index f155508ca..f13fd89c5 100644 --- a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt +++ b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt @@ -46,7 +46,6 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.lifecycle.LifecycleCoroutineScope @@ -70,6 +69,7 @@ import org.futo.inputmethod.latin.uix.actions.AllActions import org.futo.inputmethod.latin.uix.actions.EmojiAction import org.futo.inputmethod.latin.uix.settings.SettingsActivity import org.futo.inputmethod.latin.uix.theme.ThemeOption +import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.theme.UixThemeAuto import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper import org.futo.inputmethod.updates.DISABLE_UPDATE_REMINDER @@ -236,6 +236,11 @@ class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIM latinIME.latinIMELegacy.mSettings.current) } + override fun requestDialog(text: String, options: List, onCancel: () -> Unit) { + uixManager.activeDialogRequest.value = ActiveDialogRequest(text, options, onCancel) + uixManager.activeDialogRequestDismissed.value = false + } + override fun announce(s: String) { AccessibilityUtils.init(getContext()) if(AccessibilityUtils.getInstance().isAccessibilityEnabled) { @@ -244,6 +249,12 @@ class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIM } } +data class ActiveDialogRequest( + val text: String, + val options: List, + val onCancel: () -> Unit +) + class UixManager(private val latinIME: LatinIME) { private var shouldShowSuggestionStrip: Boolean = true private var suggestedWords: SuggestedWords? = null @@ -372,7 +383,7 @@ class UixManager(private val latinIME: LatinIME) { onBack = { returnBackToMainKeyboardViewFromAction() }, canExpand = currWindowAction!!.canShowKeyboard, onExpand = { toggleExpandAction() }, - windowName = windowImpl.windowName() + windowTitleBar = { windowImpl.WindowTitleBar(this) } ) } @@ -404,18 +415,18 @@ class UixManager(private val latinIME: LatinIME) { } } - private val wordBeingForgotten: MutableState = mutableStateOf(null) - private val forgetWordDismissed: MutableState = mutableStateOf(true) + val activeDialogRequest: MutableState = mutableStateOf(null) + val activeDialogRequestDismissed: MutableState = mutableStateOf(true) @Composable fun BoxScope.ForgetWordDialog() { AnimatedVisibility( - visible = !forgetWordDismissed.value, + visible = !activeDialogRequestDismissed.value, modifier = Modifier.matchParentSize(), enter = fadeIn(), exit = fadeOut() ) { - if (wordBeingForgotten.value != null) { + if (activeDialogRequest.value != null) { Box(modifier = Modifier.matchParentSize()) { Surface( color = Color.Black.copy(alpha = 0.66f), @@ -423,50 +434,34 @@ class UixManager(private val latinIME: LatinIME) { .matchParentSize() .pointerInput(Unit) { this.detectTapGestures(onPress = { - forgetWordDismissed.value = true + activeDialogRequestDismissed.value = true + activeDialogRequest.value?.onCancel?.invoke() }) } ) { } - Surface( - shape = RoundedCornerShape(16.dp), - color = MaterialTheme.colorScheme.primaryContainer, - modifier = Modifier.align(Alignment.Center) - ) { - Column(modifier = Modifier.padding(16.dp)) { - Text( - stringResource( - R.string.blacklist_from_suggestions, - wordBeingForgotten.value?.mWord!! - )) + Box(modifier = Modifier.matchParentSize().padding(8.dp)) { + Surface( + shape = RoundedCornerShape(16.dp), + color = MaterialTheme.colorScheme.primaryContainer, + modifier = Modifier.align(Alignment.Center) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + activeDialogRequest.value?.text ?: "", + style = Typography.bodyMedium + ) - Row { - TextButton( - onClick = { - forgetWordDismissed.value = true - } - ) { - Text(stringResource(R.string.cancel)) - } - - TextButton( - onClick = { - latinIME.forceForgetWord(wordBeingForgotten.value!!) - forgetWordDismissed.value = true - } - ) { - Text(stringResource(R.string.blacklist)) - } - - if(wordBeingForgotten.value!!.mKindAndFlags == SuggestedWordInfo.KIND_EMOJI_SUGGESTION) { - TextButton( - onClick = { - runBlocking { latinIME.setSetting(SHOW_EMOJI_SUGGESTIONS, false) } - forgetWordDismissed.value = true - latinIME.refreshSuggestions() + Row { + activeDialogRequest.value?.options?.forEach { + TextButton( + onClick = { + it.onClick() + activeDialogRequestDismissed.value = true + } + ) { + Text(it.option) } - ) { - Text(stringResource(R.string.disable_emoji)) } } } @@ -670,8 +665,25 @@ class UixManager(private val latinIME: LatinIME) { } fun requestForgetWord(suggestedWordInfo: SuggestedWords.SuggestedWordInfo) { - wordBeingForgotten.value = suggestedWordInfo - forgetWordDismissed.value = false + keyboardManagerForAction.requestDialog( + latinIME.getString(R.string.blacklist_from_suggestions, suggestedWordInfo.mWord), + listOf( + DialogRequestItem(latinIME.getString(R.string.cancel)) { }, + DialogRequestItem(latinIME.getString(R.string.blacklist)) { + latinIME.forceForgetWord(suggestedWordInfo) + }, + ) + if(suggestedWordInfo.mKindAndFlags == SuggestedWordInfo.KIND_EMOJI_SUGGESTION) { + listOf( + DialogRequestItem(latinIME.getString(R.string.disable_emoji)) { + runBlocking { latinIME.setSetting(SHOW_EMOJI_SUGGESTIONS, false) } + latinIME.refreshSuggestions() + } + ) + } else { + listOf() + }, + { } + ) val v = latinIME.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt index f2d0e055e..2770b1a96 100644 --- a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt +++ b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -54,6 +55,7 @@ import org.futo.inputmethod.latin.R import org.futo.inputmethod.latin.common.Constants import org.futo.inputmethod.latin.uix.Action import org.futo.inputmethod.latin.uix.ActionWindow +import org.futo.inputmethod.latin.uix.DialogRequestItem import org.futo.inputmethod.latin.uix.PersistentActionState import org.futo.inputmethod.latin.uix.PersistentStateInitialization import org.futo.inputmethod.latin.uix.SettingsKey @@ -100,7 +102,8 @@ fun ClipboardEntryView(modifier: Modifier, clipboardEntry: ClipboardEntry, onPas Surface( color = MaterialTheme.colorScheme.surfaceVariant, border = BorderStroke(2.dp, MaterialTheme.colorScheme.outlineVariant), - modifier = modifier.padding(2.dp) + modifier = modifier + .padding(2.dp) .combinedClickable( interactionSource = remember { MutableInteractionSource() }, indication = androidx.compose.material.ripple.rememberRipple(), @@ -360,6 +363,62 @@ val ClipboardHistoryAction = Action( return stringResource(R.string.clipboard_manager_action_title) } + @Composable + override fun WindowTitleBar(rowScope: RowScope) { + super.WindowTitleBar(rowScope) + + val clipboardHistory = useDataStore(ClipboardHistoryEnabled, blocking = true) + if(!clipboardHistory.value) return + + IconButton(onClick = { + val numUnpinnedItems = clipboardHistoryManager.clipboardHistory.count { !it.pinned } + if(clipboardHistoryManager.clipboardHistory.size == 0) { + manager.requestDialog( + "There are no items to clear. Disable clipboard history?", + listOf( + DialogRequestItem("Cancel") {}, + DialogRequestItem("Disable") { + clipboardHistory.setValue(false) + }, + ), + {} + ) + } else if(numUnpinnedItems == 0) { + manager.requestDialog( + "There are no unpinned items to clear. Unpin all items?", + listOf( + DialogRequestItem("Cancel") {}, + DialogRequestItem("Unpin") { + clipboardHistoryManager.clipboardHistory.toList().forEach { + if(it.pinned) { + clipboardHistoryManager.onPin(it) + } + } + }, + ), + {} + ) + } else { + manager.requestDialog( + "Clear all unpinned items?", + listOf( + DialogRequestItem("Cancel") {}, + DialogRequestItem("Clear") { + clipboardHistoryManager.clipboardHistory.toList().forEach { + if (!it.pinned) { + clipboardHistoryManager.onRemove(it) + } + } + }, + ), + {} + ) + } + }) { + Icon(painterResource(id = R.drawable.close), contentDescription = "Clear clipboard") + } + } + @Composable override fun WindowContents(keyboardShown: Boolean) { val view = LocalView.current @@ -370,7 +429,9 @@ val ClipboardHistoryAction = Action( ParagraphText("Clipboard history is not enabled. To save clipboard items, you can enable clipboard history. This will keep up to 25 items for 3 days unless pinned. Passwords and other items marked sensitive are excluded from history.") Button(onClick = { clipboardHistory.setValue(true) - }, modifier = Modifier.padding(8.dp).fillMaxWidth()) { + }, modifier = Modifier + .padding(8.dp) + .fillMaxWidth()) { Text("Enable Clipboard History") } }