mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-23 17:40:54 +00:00
store pinned clips in shared prferences instead of separate file
This commit is contained in:
parent
aad8c68bf9
commit
2dc9b12b13
4 changed files with 58 additions and 135 deletions
|
@ -2,14 +2,32 @@
|
||||||
|
|
||||||
package org.dslul.openboard.inputmethod.latin
|
package org.dslul.openboard.inputmethod.latin
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ClipboardHistoryEntry (
|
data class ClipboardHistoryEntry (
|
||||||
var timeStamp: Long,
|
var timeStamp: Long,
|
||||||
|
@Serializable(with = CharSequenceStringSerializer::class)
|
||||||
val content: CharSequence,
|
val content: CharSequence,
|
||||||
var isPinned: Boolean = false
|
var isPinned: Boolean = false
|
||||||
) : Comparable<ClipboardHistoryEntry> {
|
) : Comparable<ClipboardHistoryEntry> {
|
||||||
|
|
||||||
override fun compareTo(other: ClipboardHistoryEntry): Int {
|
override fun compareTo(other: ClipboardHistoryEntry): Int {
|
||||||
val result = other.isPinned.compareTo(isPinned)
|
val result = other.isPinned.compareTo(isPinned)
|
||||||
return if (result != 0) result else other.timeStamp.compareTo(timeStamp)
|
return if (result != 0) result else other.timeStamp.compareTo(timeStamp)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharSequenceStringSerializer : KSerializer<CharSequence> {
|
||||||
|
override val descriptor = PrimitiveSerialDescriptor("CharSequence", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: CharSequence) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder) = decoder.decodeString()
|
||||||
}
|
}
|
|
@ -5,29 +5,26 @@ package org.dslul.openboard.inputmethod.latin
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Base64
|
import kotlinx.serialization.encodeToString
|
||||||
import android.util.Log
|
import kotlinx.serialization.json.Json
|
||||||
import org.dslul.openboard.inputmethod.compat.ClipboardManagerCompat
|
import org.dslul.openboard.inputmethod.compat.ClipboardManagerCompat
|
||||||
import org.dslul.openboard.inputmethod.latin.utils.JsonUtils
|
import org.dslul.openboard.inputmethod.latin.settings.Settings
|
||||||
import java.io.File
|
import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils
|
||||||
import java.lang.Exception
|
import kotlin.collections.ArrayList
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class ClipboardHistoryManager(
|
class ClipboardHistoryManager(
|
||||||
private val latinIME: LatinIME
|
private val latinIME: LatinIME
|
||||||
) : ClipboardManager.OnPrimaryClipChangedListener {
|
) : ClipboardManager.OnPrimaryClipChangedListener {
|
||||||
|
|
||||||
private lateinit var pinnedHistoryClipsFile: File
|
|
||||||
private lateinit var clipboardManager: ClipboardManager
|
private lateinit var clipboardManager: ClipboardManager
|
||||||
private val historyEntries: MutableList<ClipboardHistoryEntry>
|
private val historyEntries: MutableList<ClipboardHistoryEntry> = ArrayList()
|
||||||
private var onHistoryChangeListener: OnHistoryChangeListener? = null
|
private var onHistoryChangeListener: OnHistoryChangeListener? = null
|
||||||
|
|
||||||
fun onCreate() {
|
fun onCreate() {
|
||||||
pinnedHistoryClipsFile = File(latinIME.filesDir, PINNED_CLIPS_DATA_FILE_NAME)
|
|
||||||
clipboardManager = latinIME.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
clipboardManager = latinIME.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
fetchPrimaryClip()
|
fetchPrimaryClip()
|
||||||
clipboardManager.addPrimaryClipChangedListener(this)
|
clipboardManager.addPrimaryClipChangedListener(this)
|
||||||
startLoadPinnedClipsFromDisk()
|
loadPinnedClips()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onPinnedClipsAvailable(pinnedClips: List<ClipboardHistoryEntry>) {
|
fun onPinnedClipsAvailable(pinnedClips: List<ClipboardHistoryEntry>) {
|
||||||
|
@ -82,7 +79,7 @@ class ClipboardHistoryManager(
|
||||||
sortHistoryEntries()
|
sortHistoryEntries()
|
||||||
val to = historyEntries.indexOf(historyEntry)
|
val to = historyEntries.indexOf(historyEntry)
|
||||||
onHistoryChangeListener?.onClipboardHistoryEntryMoved(from, to)
|
onHistoryChangeListener?.onClipboardHistoryEntryMoved(from, to)
|
||||||
startSavePinnedClipsToDisk()
|
savePinnedClips()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearHistory() {
|
fun clearHistory() {
|
||||||
|
@ -127,57 +124,16 @@ class ClipboardHistoryManager(
|
||||||
return clipData.getItemAt(0)?.coerceToText(latinIME) ?: ""
|
return clipData.getItemAt(0)?.coerceToText(latinIME) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startLoadPinnedClipsFromDisk() {
|
private fun loadPinnedClips() {
|
||||||
object : Thread("$TAG-load") {
|
val pinnedClipString = Settings.readPinnedClipString(DeviceProtectedUtils.getSharedPreferences(latinIME))
|
||||||
override fun run() {
|
if (pinnedClipString.isEmpty()) return
|
||||||
loadFromDisk()
|
val pinnedClips: List<ClipboardHistoryEntry> = Json.decodeFromString(pinnedClipString)
|
||||||
}
|
latinIME.mHandler.postUpdateClipboardPinnedClips(pinnedClips)
|
||||||
}.start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFromDisk() {
|
private fun savePinnedClips() {
|
||||||
// Debugging
|
val pinnedClips = Json.encodeToString(historyEntries.filter { it.isPinned })
|
||||||
if (pinnedHistoryClipsFile.exists() && !pinnedHistoryClipsFile.canRead()) {
|
Settings.writePinnedClipString(DeviceProtectedUtils.getSharedPreferences(latinIME), pinnedClips)
|
||||||
Log.w(TAG, "Attempt to read pinned clips file $pinnedHistoryClipsFile without permission")
|
|
||||||
}
|
|
||||||
var list = emptyList<ClipboardHistoryEntry>()
|
|
||||||
try {
|
|
||||||
if (pinnedHistoryClipsFile.exists()) {
|
|
||||||
val bytes = Base64.decode(pinnedHistoryClipsFile.readText(), Base64.DEFAULT)
|
|
||||||
list = JsonUtils.jsonBytesToHistoryEntryList(bytes)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Couldn't retrieve $pinnedHistoryClipsFile content", e)
|
|
||||||
}
|
|
||||||
latinIME.mHandler.postUpdateClipboardPinnedClips(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startSavePinnedClipsToDisk() {
|
|
||||||
val localCopy = historyEntries.filter { it.isPinned }.map { it.copy() }
|
|
||||||
object : Thread("$TAG-save") {
|
|
||||||
override fun run() {
|
|
||||||
saveToDisk(localCopy)
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveToDisk(list: List<ClipboardHistoryEntry>) {
|
|
||||||
// Debugging
|
|
||||||
if (pinnedHistoryClipsFile.exists() && !pinnedHistoryClipsFile.canWrite()) {
|
|
||||||
Log.w(TAG, "Attempt to write pinned clips file $pinnedHistoryClipsFile without permission")
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
pinnedHistoryClipsFile.createNewFile()
|
|
||||||
val jsonStr = JsonUtils.historyEntryListToJsonStr(list)
|
|
||||||
if (!TextUtils.isEmpty(jsonStr)) {
|
|
||||||
val rawText = Base64.encodeToString(jsonStr.encodeToByteArray(), Base64.DEFAULT)
|
|
||||||
pinnedHistoryClipsFile.writeText(rawText)
|
|
||||||
} else {
|
|
||||||
pinnedHistoryClipsFile.writeText("")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Couldn't write to $pinnedHistoryClipsFile", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnHistoryChangeListener {
|
interface OnHistoryChangeListener {
|
||||||
|
@ -185,13 +141,4 @@ class ClipboardHistoryManager(
|
||||||
fun onClipboardHistoryEntriesRemoved(pos: Int, count: Int)
|
fun onClipboardHistoryEntriesRemoved(pos: Int, count: Int)
|
||||||
fun onClipboardHistoryEntryMoved(from: Int, to: Int)
|
fun onClipboardHistoryEntryMoved(from: Int, to: Int)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
companion object {
|
|
||||||
const val PINNED_CLIPS_DATA_FILE_NAME = "pinned_clips.data"
|
|
||||||
const val TAG = "ClipboardHistoryManager"
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
historyEntries = LinkedList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.dslul.openboard.inputmethod.latin.utils.StatsUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -133,6 +134,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
||||||
public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
|
public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
|
||||||
public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID = "last_shown_emoji_category_page_id";
|
public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID = "last_shown_emoji_category_page_id";
|
||||||
|
|
||||||
|
public static final String PREF_PINNED_CLIPS = "pinned_clips";
|
||||||
// used as a workaround against keyboard not showing edited theme in ColorsSettingsFragment
|
// used as a workaround against keyboard not showing edited theme in ColorsSettingsFragment
|
||||||
public static final String PREF_FORCE_OPPOSITE_THEME = "force_opposite_theme";
|
public static final String PREF_FORCE_OPPOSITE_THEME = "force_opposite_theme";
|
||||||
|
|
||||||
|
@ -147,6 +149,16 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
||||||
|
|
||||||
private static final Settings sInstance = new Settings();
|
private static final Settings sInstance = new Settings();
|
||||||
|
|
||||||
|
// preferences that are not used in SettingsValues
|
||||||
|
private static final HashSet<String> dontReloadOnChanged = new HashSet<>() {{
|
||||||
|
add(PREF_FORCE_OPPOSITE_THEME);
|
||||||
|
add(PREF_PINNED_CLIPS);
|
||||||
|
add(PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID);
|
||||||
|
add(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID);
|
||||||
|
add(PREF_EMOJI_RECENT_KEYS);
|
||||||
|
add(PREF_DONT_SHOW_MISSING_DICTIONARY_DIALOG);
|
||||||
|
}};
|
||||||
|
|
||||||
public static Settings getInstance() {
|
public static Settings getInstance() {
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
@ -172,6 +184,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
|
public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
|
||||||
|
if (dontReloadOnChanged.contains(key))
|
||||||
|
return;
|
||||||
mSettingsValuesLock.lock();
|
mSettingsValuesLock.lock();
|
||||||
try {
|
try {
|
||||||
if (mSettingsValues == null) {
|
if (mSettingsValues == null) {
|
||||||
|
@ -425,6 +439,14 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
||||||
return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID, defValue);
|
return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID, defValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String readPinnedClipString(final SharedPreferences prefs) {
|
||||||
|
return prefs.getString(PREF_PINNED_CLIPS, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writePinnedClipString(final SharedPreferences prefs, final String clips) {
|
||||||
|
prefs.edit().putString(PREF_PINNED_CLIPS, clips).apply();
|
||||||
|
}
|
||||||
|
|
||||||
public static List<String> readPinnedKeys(final SharedPreferences prefs) {
|
public static List<String> readPinnedKeys(final SharedPreferences prefs) {
|
||||||
final String pinnedKeysString = prefs.getString(Settings.PREF_PINNED_KEYS, "");
|
final String pinnedKeysString = prefs.getString(Settings.PREF_PINNED_KEYS, "");
|
||||||
if (pinnedKeysString.isEmpty())
|
if (pinnedKeysString.isEmpty())
|
||||||
|
|
|
@ -6,20 +6,15 @@
|
||||||
|
|
||||||
package org.dslul.openboard.inputmethod.latin.utils;
|
package org.dslul.openboard.inputmethod.latin.utils;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.JsonReader;
|
import android.util.JsonReader;
|
||||||
import android.util.JsonWriter;
|
import android.util.JsonWriter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import org.dslul.openboard.inputmethod.latin.ClipboardHistoryEntry;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -28,8 +23,6 @@ public final class JsonUtils {
|
||||||
|
|
||||||
private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName();
|
private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName();
|
||||||
private static final String STRING_CLASS_NAME = String.class.getSimpleName();
|
private static final String STRING_CLASS_NAME = String.class.getSimpleName();
|
||||||
private static final String CLIPBOARD_HISTORY_ENTRY_ID_KEY = "id";
|
|
||||||
private static final String CLIPBOARD_HISTORY_ENTRY_CONTENT_KEY = "content";
|
|
||||||
|
|
||||||
private static final String EMPTY_STRING = "";
|
private static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
|
@ -88,63 +81,6 @@ public final class JsonUtils {
|
||||||
return EMPTY_STRING;
|
return EMPTY_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ClipboardHistoryEntry> jsonBytesToHistoryEntryList(final byte[] bytes) {
|
|
||||||
final ArrayList<ClipboardHistoryEntry> list = new ArrayList<>();
|
|
||||||
final JsonReader reader = new JsonReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
|
|
||||||
try {
|
|
||||||
reader.beginArray();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
reader.beginObject();
|
|
||||||
long id = 0;
|
|
||||||
String content = EMPTY_STRING;
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
final String name = reader.nextName();
|
|
||||||
if (name.equals(CLIPBOARD_HISTORY_ENTRY_ID_KEY)) {
|
|
||||||
id = reader.nextLong();
|
|
||||||
} else if (name.equals(CLIPBOARD_HISTORY_ENTRY_CONTENT_KEY)) {
|
|
||||||
content = reader.nextString();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Invalid name: " + name);
|
|
||||||
reader.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (id > 0 && !TextUtils.isEmpty(content)) {
|
|
||||||
list.add(new ClipboardHistoryEntry(id, content, true));
|
|
||||||
}
|
|
||||||
reader.endObject();
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return list;
|
|
||||||
} catch (final IOException e) {
|
|
||||||
} finally {
|
|
||||||
close(reader);
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String historyEntryListToJsonStr(final Collection<ClipboardHistoryEntry> entries) {
|
|
||||||
if (entries == null || entries.isEmpty()) {
|
|
||||||
return EMPTY_STRING;
|
|
||||||
}
|
|
||||||
final StringWriter sw = new StringWriter();
|
|
||||||
final JsonWriter writer = new JsonWriter(sw);
|
|
||||||
try {
|
|
||||||
writer.beginArray();
|
|
||||||
for (final ClipboardHistoryEntry e : entries) {
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name(CLIPBOARD_HISTORY_ENTRY_ID_KEY).value(e.getTimeStamp());
|
|
||||||
writer.name(CLIPBOARD_HISTORY_ENTRY_CONTENT_KEY).value(e.getContent().toString());
|
|
||||||
writer.endObject();
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
return sw.toString();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
} finally {
|
|
||||||
close(writer);
|
|
||||||
}
|
|
||||||
return EMPTY_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void close(final Closeable closeable) {
|
private static void close(final Closeable closeable) {
|
||||||
try {
|
try {
|
||||||
if (closeable != null) {
|
if (closeable != null) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue