From 209cc6fd606ca404757b8b383cbb0a093a01abc2 Mon Sep 17 00:00:00 2001 From: "Donald H." Date: Wed, 11 Feb 2026 11:27:47 -0500 Subject: [PATCH 1/2] Invalidate QuickClip cache when clipboard items are removed When users delete clipboard items from clipboard history, the QuickClip cache could still hold references to the deleted items, causing them to appear as suggestions in the action bar. This fix invalidates the cached state in QuickClip when items are explicitly removed via onRemove() and when items are pruned by pruneOldItems(), ensuring deleted clipboard content no longer surfaces as QuickClip suggestions. Fixes #1766 Co-Authored-By: Claude Opus 4.6 --- .../futo/inputmethod/latin/uix/QuickClip.kt | 7 +++++++ .../uix/actions/ClipboardHistoryAction.kt | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/java/src/org/futo/inputmethod/latin/uix/QuickClip.kt b/java/src/org/futo/inputmethod/latin/uix/QuickClip.kt index aeb88e8e23..bc9d61e153 100644 --- a/java/src/org/futo/inputmethod/latin/uix/QuickClip.kt +++ b/java/src/org/futo/inputmethod/latin/uix/QuickClip.kt @@ -215,6 +215,13 @@ object QuickClip { timeOfDismissal = System.currentTimeMillis() } + // Invalidate the cached QuickClip state. This must be called when clipboard items + // are removed or pruned so that deleted items no longer appear as QuickClip suggestions. + fun invalidateCache() { + cachedPreviousItem = null + cachedPreviousState = null + } + private fun getStateForItem(validUntil: Long, mimeTypes: List, item: ClipData.Item, isSensitive: Boolean): QuickClipState? { val texts = mutableListOf() val currTexts = mutableSetOf() 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 48b1add48f..73b017c920 100644 --- a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt +++ b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt @@ -65,6 +65,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.QuickClip import org.futo.inputmethod.latin.uix.DialogRequestItem import org.futo.inputmethod.latin.uix.PersistentActionState import org.futo.inputmethod.latin.uix.PersistentStateInitialization @@ -380,14 +381,15 @@ class ClipboardHistoryManager(val context: Context, val coroutineScope: Lifecycl val numHoursToKeep = context.getSetting(ClipboardHistoryTimeToKeep) val numItemsToKeep = context.getSetting(ClipboardHistoryItemsToKeep) val minimumTimestamp = System.currentTimeMillis() - (numHoursToKeep * 60L * 60L * 1000L) - clipboardHistory.removeAll { + val removedByTime = clipboardHistory.removeAll { (!it.pinned) && (it.timestamp < minimumTimestamp) } // Remove duplicates of entries, if any appeared // Duplicates will have same timestamp, same text, etc val set = clipboardHistory.toSet() - if(set.size < clipboardHistory.size) { + val hadDuplicates = set.size < clipboardHistory.size + if(hadDuplicates) { clipboardHistory.clear() clipboardHistory.addAll(set) } @@ -395,14 +397,21 @@ class ClipboardHistoryManager(val context: Context, val coroutineScope: Lifecycl val maxItems = numItemsToKeep val numUnpinnedItems = clipboardHistory.filter { !it.pinned }.size + var removedByLimit = false val numItemsToRemove = numUnpinnedItems - maxItems if(numItemsToRemove > 0) { for(i in 0 until numItemsToRemove) { val idx = clipboardHistory.indexOfFirst { !it.pinned } if(idx == -1) break clipboardHistory.removeAt(idx) + removedByLimit = true } } + + // Invalidate QuickClip cache if any items were removed during pruning + if(removedByTime || hadDuplicates || removedByLimit) { + QuickClip.invalidateCache() + } } var saveClipboardLoadJob: Job? = null @@ -598,6 +607,11 @@ ${if(clipboardFileSwap.exists()) { clipboardFileSwap.readText() } else { "File d } } clipboardHistory.removeAll { it == item } + + // Invalidate QuickClip cache so removed items no longer appear as suggestions + QuickClip.invalidateCache() + QuickClip.markQuickClipDismissed() + saveClipboard() } From 0d9bc2ece1ca0f4794cc76a593e5fc5589448154 Mon Sep 17 00:00:00 2001 From: "Donald H." Date: Wed, 11 Feb 2026 12:02:54 -0500 Subject: [PATCH 2/2] Dismiss QuickClip UI state when clipboard items are deleted The previous commit cleared the QuickClip singleton cache but not the Compose MutableState in UixManager that drives the actual UI. This caused the stale suggestion pill to reappear when closing the clipboard panel back to the main keyboard view within the same input session. Exposes dismissQuickClips() through KeyboardManagerForAction and calls it from both the single-item delete and bulk clear-unpinned paths. Co-Authored-By: Claude Opus 4.6 --- java/src/org/futo/inputmethod/latin/uix/Action.kt | 1 + java/src/org/futo/inputmethod/latin/uix/UixManager.kt | 4 ++++ .../inputmethod/latin/uix/actions/ClipboardHistoryAction.kt | 2 ++ 3 files changed, 7 insertions(+) diff --git a/java/src/org/futo/inputmethod/latin/uix/Action.kt b/java/src/org/futo/inputmethod/latin/uix/Action.kt index 1b6f2556f1..cde0e41ef3 100644 --- a/java/src/org/futo/inputmethod/latin/uix/Action.kt +++ b/java/src/org/futo/inputmethod/latin/uix/Action.kt @@ -105,6 +105,7 @@ interface KeyboardManagerForAction { fun copyToClipboard(cut: Boolean = false) fun pasteFromClipboard() + fun dismissQuickClips() // Returns null if the current IME is not of this kind. // TODO: In the future make an IMEActionInterface for correctness diff --git a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt index 831f0ff540..347b56fc29 100644 --- a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt +++ b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt @@ -512,6 +512,10 @@ class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIM uixManager.dismissQuickClips() } + override fun dismissQuickClips() { + uixManager.dismissQuickClips() + } + override fun getSizingCalculator(): KeyboardSizingCalculator = latinIME.sizingCalculator 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 73b017c920..6dd419192f 100644 --- a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt +++ b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt @@ -709,6 +709,7 @@ val ClipboardHistoryAction = Action( clipboardHistoryManager.onRemove(it) } } + manager.dismissQuickClips() }, ), {} @@ -855,6 +856,7 @@ val ClipboardHistoryAction = Action( context.getString(R.string.action_clipboard_manager_remove_item) ) { clipboardHistoryManager.onRemove(it) + manager.dismissQuickClips() manager.performHapticAndAudioFeedback(Constants.CODE_TAB, view) } )