Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class ClipboardHistoryManager(
super.onChange(selfChange, uri)
if (latinIME.mSettings.current.mSuggestScreenshots) {
updateLatestScreenshotCache {
// Race guard (field NPE 22 Aug 2026): a screenshot can land before the
// keyboard view exists — getSuggestionStripView() must not run on a
// half-built IME. Skip; the next input show will surface the suggestion.
if (!latinIME.isInputViewShown) return@updateLatestScreenshotCache
latinIME.tryShowClipboardSuggestion()
}
}
Expand Down Expand Up @@ -553,16 +557,18 @@ class ClipboardHistoryManager(
latinIME.mSettings.getCustomTypeface()?.let { textView.typeface = it }
textView.text = (if (isClipSensitive(inputType)) "*".repeat(content.length) else content)
.take(200) // truncate displayed text for performance reasons
val clipIcon = latinIME.mKeyboardSwitcher.keyboard.mIconsSet.getIconDrawable(ToolbarKey.PASTE.name.lowercase())
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(clipIcon, null, null, null)
val clipIcon = activeKeyboardIcons()?.getIconDrawable(ToolbarKey.PASTE.name.lowercase())
clipIcon?.let { textView.setCompoundDrawablesRelativeWithIntrinsicBounds(it, null, null, null) }
textView.setOnClickListener {
dontShowCurrentSuggestion = true
latinIME.onTextInput(content.toString())
AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, it, HapticEvent.KEY_PRESS)
binding.root.isGone = true
}
val closeButton = binding.clipboardSuggestionClose
closeButton.setImageDrawable(latinIME.mKeyboardSwitcher.keyboard.mIconsSet.getIconDrawable(ToolbarKey.CLOSE_HISTORY.name.lowercase()))
activeKeyboardIcons()?.getIconDrawable(ToolbarKey.CLOSE_HISTORY.name.lowercase())?.let { closeIcon ->
closeButton.setImageDrawable(closeIcon)
}
closeButton.setOnClickListener {
val prefs = latinIME.prefs()
prefs.edit().putString("last_dismissed_clipboard_text", content.toString()).apply()
Expand All @@ -581,6 +587,19 @@ class ClipboardHistoryManager(

private var lastSuggestedScreenshotUri: String? = null

/** Safe access to the active keyboard's icon set. The keyboard view is built lazily and torn
* down with input views, so a spontaneous clipboard/screenshot callback can run while it does
* not exist (field NPE 22 Aug 2026: Keyboard.mIconsSet read on null reference). Returns null
* instead of crashing; callers degrade to an icon-less suggestion. */
private fun activeKeyboardIcons(): helium314.keyboard.keyboard.internal.KeyboardIconsSet? {
return try {
val kb = latinIME.mKeyboardSwitcher.keyboard ?: return null
kb.mIconsSet
} catch (e: Exception) {
null
}
}

private fun getScreenshotSuggestionView(parent: ViewGroup?): View? {
if (parent == null || dontShowCurrentSuggestion) return null

Expand Down Expand Up @@ -642,8 +661,9 @@ class ClipboardHistoryManager(
val drawable = android.graphics.drawable.BitmapDrawable(latinIME.resources, croppedThumb)
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null)
} catch (e: Exception) {
val clipIcon = latinIME.mKeyboardSwitcher.keyboard.mIconsSet.getIconDrawable(ToolbarKey.PASTE.name.lowercase())
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(clipIcon, null, null, null)
activeKeyboardIcons()?.getIconDrawable(ToolbarKey.PASTE.name.lowercase())?.let { clipIcon ->
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(clipIcon, null, null, null)
}
}
}

Expand All @@ -656,7 +676,9 @@ class ClipboardHistoryManager(
}

val closeButton = binding.clipboardSuggestionClose
closeButton.setImageDrawable(latinIME.mKeyboardSwitcher.keyboard.mIconsSet.getIconDrawable(ToolbarKey.CLOSE_HISTORY.name.lowercase()))
activeKeyboardIcons()?.getIconDrawable(ToolbarKey.CLOSE_HISTORY.name.lowercase())?.let { closeIcon ->
closeButton.setImageDrawable(closeIcon)
}
closeButton.setOnClickListener {
val prefs = latinIME.prefs()
val rawDeletedSet = prefs.getStringSet("deleted_screenshot_uris", emptySet()) ?: emptySet()
Expand Down