Add a button to clear clipboard history

This commit is contained in:
Aleksandras Kostarevas 2024-06-20 23:20:46 +03:00
parent 1ae29aa9f1
commit ed550eec4b
4 changed files with 146 additions and 58 deletions

View File

@ -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<DialogRequestItem>, 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()
}

View File

@ -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) {

View File

@ -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<DialogRequestItem>, 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<DialogRequestItem>,
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<SuggestedWordInfo?> = mutableStateOf(null)
private val forgetWordDismissed: MutableState<Boolean> = mutableStateOf(true)
val activeDialogRequest: MutableState<ActiveDialogRequest?> = mutableStateOf(null)
val activeDialogRequestDismissed: MutableState<Boolean> = 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) {

View File

@ -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")
}
}