Skip to content

Commit 574eb35

Browse files
4di-helloclaude
andcommitted
Add option to not keep passwords and one-time codes in clipboard history
Skips clips marked as sensitive by the copying app (ClipDescription EXTRA_IS_SENSITIVE, e.g. password managers) and one-time codes (4 to 8 digits). Off by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VuiaMhLXwhErf9shhrr1uD
1 parent 1e512a9 commit 574eb35

9 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ class ClipboardHistoryManager(
418418
}
419419

420420
if (TextUtils.isEmpty(content) && imageUri == null) return
421+
// passwords (marked as sensitive by the copying app) and one-time codes are not kept in the history
422+
if (latinIME.mSettings.current.mClipboardSkipSensitive && imageUri == null
423+
&& isSensitiveForHistory(clipData.description, content)) return
421424
clipboardDao?.addClip(timeStamp, false, content, imageUri)
422425
}
423426
}
@@ -584,6 +587,10 @@ class ClipboardHistoryManager(
584587
return clipData.getItemAt(0)?.coerceToText(latinIME) ?: ""
585588
}
586589

590+
/** Clip marked as sensitive (e.g. by password managers), or a one-time code (4 to 8 digits) */
591+
private fun isSensitiveForHistory(description: android.content.ClipDescription?, content: String): Boolean =
592+
ClipboardManagerCompat.getClipSensitivity(description) == true || isOneTimeCode(content)
593+
587594
private fun isClipSensitive(inputType: Int): Boolean {
588595
ClipboardManagerCompat.getClipSensitivity(clipboardManager.primaryClip?.description)?.let { return it }
589596
return InputTypeUtils.isPasswordInputType(inputType)
@@ -801,5 +808,8 @@ class ClipboardHistoryManager(
801808
companion object {
802809
const val RECENT_TIME_MILLIS = 1 * 60 * 1000L // 1 minute (for clipboard suggestions)
803810
const val RECENT_SCREENSHOT_TIME_MILLIS = 5 * 60 * 1000L // 5 minutes
811+
private val ONE_TIME_CODE = Regex("\\d{4,8}")
812+
fun isOneTimeCode(text: String) = ONE_TIME_CODE.matches(text.trim())
804813
}
814+
805815
}

‎app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ object Defaults {
215215
const val PREF_TOUCHPAD_FULLSCREEN = false
216216
const val PREF_FORCE_AUTO_CAPS = false
217217
const val PREF_SHIFT_RECAPITALIZES_WORD = false
218+
const val PREF_CLIPBOARD_SKIP_SENSITIVE = false
218219
const val PREF_OFFLINE_TEMP = 0.1f // Lower for faster, more deterministic proofreading
219220
const val PREF_OFFLINE_TOP_P = 0.5f // Lower for faster token sampling
220221
const val PREF_OFFLINE_TOP_K = 40

‎app/src/main/java/helium314/keyboard/latin/settings/Settings.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ class Settings private constructor() : SharedPreferences.OnSharedPreferenceChang
484484
const val PREF_PERSIST_TEXT_EDIT_MODE = "persist_text_edit_mode"
485485
const val PREF_FORCE_AUTO_CAPS = "force_auto_caps"
486486
const val PREF_SHIFT_RECAPITALIZES_WORD = "shift_recapitalizes_word"
487+
const val PREF_CLIPBOARD_SKIP_SENSITIVE = "clipboard_skip_sensitive"
487488
const val PREF_OFFLINE_TEMP = "offline_temp"
488489
const val PREF_OFFLINE_TOP_P = "offline_top_p"
489490
const val PREF_OFFLINE_TOP_K = "offline_top_k"

‎app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.kt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ open class SettingsValues(
105105
val mTouchpadFullscreen: Boolean
106106
val mForceAutoCaps: Boolean
107107
val mShiftRecapitalizesWord: Boolean
108+
val mClipboardSkipSensitive: Boolean
108109
val mDeleteSwipeEnabled: Boolean
109110
val mDeleteSwipeWordByWord: Boolean
110111
val mAutospaceAfterPunctuation: Boolean
@@ -446,6 +447,7 @@ open class SettingsValues(
446447
mTouchpadFullscreen = prefs.getBoolean(Settings.PREF_TOUCHPAD_FULLSCREEN, Defaults.PREF_TOUCHPAD_FULLSCREEN)
447448
mForceAutoCaps = prefs.getBoolean(Settings.PREF_FORCE_AUTO_CAPS, Defaults.PREF_FORCE_AUTO_CAPS)
448449
mShiftRecapitalizesWord = prefs.getBoolean(Settings.PREF_SHIFT_RECAPITALIZES_WORD, Defaults.PREF_SHIFT_RECAPITALIZES_WORD)
450+
mClipboardSkipSensitive = prefs.getBoolean(Settings.PREF_CLIPBOARD_SKIP_SENSITIVE, Defaults.PREF_CLIPBOARD_SKIP_SENSITIVE)
449451
mDeleteSwipeEnabled = prefs.getBoolean(Settings.PREF_DELETE_SWIPE, Defaults.PREF_DELETE_SWIPE)
450452
mDeleteSwipeWordByWord = prefs.getBoolean(Settings.PREF_DELETE_SWIPE_WORD_BY_WORD, Defaults.PREF_DELETE_SWIPE_WORD_BY_WORD)
451453
mAutospaceAfterPunctuation = prefs.getBoolean(Settings.PREF_AUTOSPACE_AFTER_PUNCTUATION, Defaults.PREF_AUTOSPACE_AFTER_PUNCTUATION)

‎app/src/main/java/helium314/keyboard/settings/preferences/BackupRestorePreference.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ private fun getCategoryForPrefKey(key: String): BackupCategory {
621621
val clipboardKeys = setOf(
622622
"enable_clipboard_history", "suggest_screenshots", "compress_screenshots",
623623
"clipboard_history_retention_time", "clipboard_history_pinned_first",
624-
"clipboard_fold_pinned", "clear_clipboard_icon"
624+
"clipboard_fold_pinned", "clear_clipboard_icon", "clipboard_skip_sensitive"
625625
)
626626
if (clipboardKeys.contains(key)) return BackupCategory.CLIPBOARD
627627

‎app/src/main/java/helium314/keyboard/settings/screens/PreferencesScreen.kt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ fun PreferencesScreen(
9999
R.string.settings_category_clipboard_history,
100100
Settings.PREF_ENABLE_CLIPBOARD_HISTORY,
101101
if (clipboardHistoryEnabled) Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME else null,
102+
if (clipboardHistoryEnabled) Settings.PREF_CLIPBOARD_SKIP_SENSITIVE else null,
102103
if (clipboardHistoryEnabled) Settings.PREF_CLIPBOARD_HISTORY_PINNED_FIRST else null,
103104
if (clipboardHistoryEnabled) Settings.PREF_CLIPBOARD_FOLD_PINNED else null,
104105
)
@@ -220,6 +221,10 @@ fun createPreferencesSettings(context: Context) = listOf(
220221
val ctx = LocalContext.current
221222
SwitchPreference(it, Defaults.PREF_ENABLE_CLIPBOARD_HISTORY) { ClipboardDao.getInstance(ctx)?.clearNonPinned() }
222223
},
224+
Setting(context, Settings.PREF_CLIPBOARD_SKIP_SENSITIVE,
225+
R.string.clipboard_skip_sensitive, R.string.clipboard_skip_sensitive_summary) {
226+
SwitchPreference(it, Defaults.PREF_CLIPBOARD_SKIP_SENSITIVE)
227+
},
223228
Setting(context, Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, R.string.clipboard_history_retention_time) { setting ->
224229
val ctx = LocalContext.current
225230
SliderPreference(

‎app/src/main/res/values-de/strings.xml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,4 +1734,6 @@ Ermöglicht lokale GGUF / llama.cpp-Inferenz für Korrekturlesen und Umschreiben
17341734
<string name="backup_api_keys_warning_confirm">API-Schlüssel einschließen</string>
17351735
<string name="gif_get_api_key">Holen Sie sich den API-Schlüssel</string>
17361736
<string name="gif_provider_disabled">Deaktiviert</string>
1737+
<string name="clipboard_skip_sensitive">Passwörter und Codes nicht speichern</string>
1738+
<string name="clipboard_skip_sensitive_summary">Als vertraulich markierte Einträge (z. B. aus Passwort-Managern) und Einmalcodes mit 4 bis 8 Ziffern landen nicht im Verlauf. Einfügen funktioniert weiterhin</string>
17371739
</resources>

‎app/src/main/res/values/strings.xml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2418,4 +2418,6 @@ New dictionary:
24182418
<string name="gif_no_recent_items">No recent items</string>
24192419
<string name="gif_manage_local_media">Manage Local Media</string>
24202420
<string name="gif_get_api_key">Get API key</string>
2421+
<string name="clipboard_skip_sensitive">Don\'t keep passwords and codes</string>
2422+
<string name="clipboard_skip_sensitive_summary">Entries marked as confidential (e.g. from password managers) and one-time codes with 4 to 8 digits are not added to the history. Pasting still works</string>
24212423
</resources>

‎app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,14 @@ class InputLogicTest {
386386
assertEquals("hello ", text)
387387
}
388388

389+
@Test fun `one-time codes are detected for the clipboard history`() {
390+
assertEquals(true, ClipboardHistoryManager.isOneTimeCode("123456"))
391+
assertEquals(true, ClipboardHistoryManager.isOneTimeCode(" 4821 "))
392+
assertEquals(false, ClipboardHistoryManager.isOneTimeCode("123"))
393+
assertEquals(false, ClipboardHistoryManager.isOneTimeCode("0611 123456"))
394+
assertEquals(false, ClipboardHistoryManager.isOneTimeCode("abc123"))
395+
}
396+
389397
@Test fun `shift at end of word does not change the word by default`() {
390398
reset()
391399
chainInput("hello")

0 commit comments

Comments
 (0)