diff --git a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt index 7e8ae9f8b..7f5121f6a 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt @@ -174,7 +174,7 @@ class ClipboardHistoryView @JvmOverloads constructor( ) { initialize() setupToolbarKeys() - historyManager.prepareClipboardHistory() + historyManager.cancelRetentionCheck() historyManager.setHistoryChangeListener(this) clipboardHistoryManager = historyManager clipboardAdapter.clipboardHistoryManager = historyManager @@ -202,6 +202,7 @@ class ClipboardHistoryView @JvmOverloads constructor( fun stopClipboardHistory() { if (!initialized) return clipboardRecyclerView.adapter = null + clipboardHistoryManager?.scheduleRetentionCheck() clipboardHistoryManager?.setHistoryChangeListener(null) clipboardHistoryManager = null clipboardAdapter.clipboardHistoryManager = null diff --git a/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt b/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt index 625791eb7..fa6e33c02 100644 --- a/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt +++ b/app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt @@ -4,27 +4,45 @@ package helium314.keyboard.latin import android.content.ClipboardManager import android.content.Context +import android.content.SharedPreferences +import android.os.Handler +import android.os.Looper import android.text.TextUtils import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import helium314.keyboard.compat.ClipboardManagerCompat import helium314.keyboard.latin.settings.Settings +import helium314.keyboard.latin.utils.DeviceProtectedUtils import kotlin.collections.ArrayList class ClipboardHistoryManager( private val latinIME: LatinIME -) : ClipboardManager.OnPrimaryClipChangedListener { +) : ClipboardManager.OnPrimaryClipChangedListener, SharedPreferences.OnSharedPreferenceChangeListener { private lateinit var clipboardManager: ClipboardManager private var onHistoryChangeListener: OnHistoryChangeListener? = null + private var isRetentionCheckScheduled = false + private var clipboardHistoryEnabled = true + private var maxClipRetentionTime = DEFAULT_RETENTION_TIME_MIN * ONE_MINUTE_MILLIS + private val retentionCheckHandler = Handler(Looper.getMainLooper()) + private val retentionCheckRunnable: Runnable = object : Runnable { + override fun run() { + checkClipRetentionElapsed() + retentionCheckHandler.postDelayed(this, maxClipRetentionTime) + } + } fun onCreate() { clipboardManager = latinIME.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboardHistoryEnabled = latinIME.mSettings.current.mClipboardHistoryEnabled + maxClipRetentionTime = latinIME.mSettings.current.mClipboardHistoryRetentionTime * ONE_MINUTE_MILLIS clipboardManager.addPrimaryClipChangedListener(this) if (historyEntries.isEmpty()) loadPinnedClips() if (latinIME.mSettings.current?.mClipboardHistoryEnabled == true) fetchPrimaryClip() + scheduleRetentionCheck() + DeviceProtectedUtils.getSharedPreferences(latinIME).registerOnSharedPreferenceChangeListener(this) } fun onPinnedClipsAvailable(pinnedClips: List) { @@ -39,15 +57,41 @@ class ClipboardHistoryManager( fun onDestroy() { clipboardManager.removePrimaryClipChangedListener(this) + cancelRetentionCheck() } override fun onPrimaryClipChanged() { // Make sure we read clipboard content only if history settings is set - if (latinIME.mSettings.current?.mClipboardHistoryEnabled == true) { + if (clipboardHistoryEnabled) { fetchPrimaryClip() } } + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + when (key) { + Settings.PREF_ENABLE_CLIPBOARD_HISTORY -> { + clipboardHistoryEnabled = Settings.readClipboardHistoryEnabled(sharedPreferences) + if (clipboardHistoryEnabled) { + scheduleRetentionCheck() + } else { + cancelRetentionCheck() + clearHistory() + } + } + Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME -> { + val newRetentionTimeMinutes = + sharedPreferences?.getInt(key, DEFAULT_RETENTION_TIME_MIN) ?: return + if (newRetentionTimeMinutes == 0) { + cancelRetentionCheck() + } + else if (maxClipRetentionTime == 0L) { // retention limit has been enabled + scheduleRetentionCheck(newRetentionTimeMinutes * ONE_MINUTE_MILLIS) + } + maxClipRetentionTime = newRetentionTimeMinutes * ONE_MINUTE_MILLIS + } + } + } + private fun fetchPrimaryClip() { val clipData = clipboardManager.primaryClip ?: return if (clipData.itemCount == 0 || clipData.description?.hasMimeType("text/*") == false) return @@ -113,16 +157,24 @@ class ClipboardHistoryManager( } private fun checkClipRetentionElapsed() { - val mins = latinIME.mSettings.current.mClipboardHistoryRetentionTime - if (mins <= 0) return // No retention limit - val maxClipRetentionTime = mins * 60 * 1000L val now = System.currentTimeMillis() + if (latinIME.mSettings.current?.mClearPrimaryClipboard == true) { + ClipboardManagerCompat.clearPrimaryClip(clipboardManager) + } historyEntries.removeAll { !it.isPinned && (now - it.timeStamp) > maxClipRetentionTime } } - // We do not want to update history while user is visualizing it, so we check retention only - // when history is about to be shown - fun prepareClipboardHistory() = checkClipRetentionElapsed() + fun scheduleRetentionCheck(timeMillis: Long = maxClipRetentionTime) { + if (isRetentionCheckScheduled || !clipboardHistoryEnabled || timeMillis <= 0) return + retentionCheckHandler.postDelayed(retentionCheckRunnable, timeMillis) + isRetentionCheckScheduled = true + } + + fun cancelRetentionCheck() { + if (!isRetentionCheckScheduled) return + retentionCheckHandler.removeCallbacksAndMessages(null) + isRetentionCheckScheduled = false + } fun getHistorySize() = historyEntries.size @@ -162,5 +214,7 @@ class ClipboardHistoryManager( companion object { // store pinned clips in companion object so they survive a keyboard switch (which destroys the current instance) private val historyEntries: MutableList = ArrayList() + private const val ONE_MINUTE_MILLIS = 60 * 1000L + private const val DEFAULT_RETENTION_TIME_MIN = 10 } } diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index cc40c8cb3..a2d7b1e5d 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -584,9 +584,9 @@ public void onCreate() { KeyboardSwitcher.init(this); super.onCreate(); - mClipboardHistoryManager.onCreate(); mHandler.onCreate(); loadSettings(); + mClipboardHistoryManager.onCreate(); // Register to receive ringer mode change. final IntentFilter filter = new IntentFilter(); diff --git a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java index 528e06f18..743ed2c5b 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java @@ -126,6 +126,9 @@ private void refreshEnablingsOfKeypressSoundAndVibrationAndHistRetentionSettings Settings.readKeypressSoundEnabled(prefs, res)); setPreferenceVisible(Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, Settings.readClipboardHistoryEnabled(prefs)); + setPreferenceVisible(Settings.PREF_CLEAR_PRIMARY_CLIPBOARD, + Settings.readClipboardHistoryEnabled(prefs) + && Settings.readClipboardHistoryRetentionTime(prefs, res) > 0); } private void setupKeypressVibrationDurationSettings() { diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 4565001f8..c0385c68b 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -136,6 +136,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_ENABLE_CLIPBOARD_HISTORY = "enable_clipboard_history"; public static final String PREF_CLIPBOARD_HISTORY_RETENTION_TIME = "clipboard_history_retention_time"; + public static final String PREF_CLEAR_PRIMARY_CLIPBOARD = "clear_primary_clipboard"; public static final String PREF_SECONDARY_LOCALES_PREFIX = "secondary_locales_"; public static final String PREF_ADD_TO_PERSONAL_DICTIONARY = "add_to_personal_dictionary"; @@ -387,6 +388,10 @@ public static int readDefaultClipboardHistoryRetentionTime(final Resources res) return res.getInteger(R.integer.config_clipboard_history_retention_time); } + public static boolean readClearPrimaryClipboard(final SharedPreferences prefs) { + return prefs.getBoolean(PREF_CLEAR_PRIMARY_CLIPBOARD, false); + } + public static int readHorizontalSpaceSwipe(final SharedPreferences prefs) { return switch (prefs.getString(PREF_SPACE_HORIZONTAL_SWIPE, "none")) { case "move_cursor" -> KeyboardActionListener.SWIPE_MOVE_CURSOR; diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index 111a67c6f..aab67a41f 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -78,6 +78,7 @@ public class SettingsValues { public final boolean mAutospaceAfterPunctuationEnabled; public final boolean mClipboardHistoryEnabled; public final long mClipboardHistoryRetentionTime; + public final boolean mClearPrimaryClipboard; public final boolean mOneHandedModeEnabled; public final int mOneHandedModeGravity; public final float mOneHandedModeScale; @@ -200,7 +201,7 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina mAutospaceAfterPunctuationEnabled = Settings.readAutospaceAfterPunctuationEnabled(prefs); mClipboardHistoryEnabled = Settings.readClipboardHistoryEnabled(prefs); mClipboardHistoryRetentionTime = Settings.readClipboardHistoryRetentionTime(prefs, res); - + mClearPrimaryClipboard = Settings.readClearPrimaryClipboard(prefs); mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs, mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs, mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); if (mOneHandedModeEnabled) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e1ce7785b..900574172 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,6 +148,8 @@ If disabled, clipboard key will paste clipboard content if any History retention time + + Clear primary clipboard Delete swipe diff --git a/app/src/main/res/xml/prefs_screen_preferences.xml b/app/src/main/res/xml/prefs_screen_preferences.xml index bf3cc337f..7dc08e408 100644 --- a/app/src/main/res/xml/prefs_screen_preferences.xml +++ b/app/src/main/res/xml/prefs_screen_preferences.xml @@ -122,6 +122,12 @@ android:title="@string/clipboard_history_retention_time" latin:maxValue="120" /> + +