Persist quick send history safely

This commit is contained in:
Misaka
2026-05-17 17:10:01 +08:00
parent 19441399d0
commit 5f6e045e98
2 changed files with 78 additions and 7 deletions

View File

@@ -21,6 +21,7 @@ import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Switch import android.widget.Switch
import android.widget.TextView import android.widget.TextView
import org.json.JSONArray
class QuickSendActivity : Activity() { class QuickSendActivity : Activity() {
private lateinit var input: EditText private lateinit var input: EditText
@@ -459,6 +460,7 @@ class QuickSendActivity : Activity() {
} }
if (ConnectionService.sendText(text) && (!appendEnter || ConnectionService.sendEnter())) { if (ConnectionService.sendText(text) && (!appendEnter || ConnectionService.sendEnter())) {
saveHistory(text)
sendButton.background = roundedDrawable(Color.rgb(22, 163, 74), dp(26).toFloat()) sendButton.background = roundedDrawable(Color.rgb(22, 163, 74), dp(26).toFloat())
sendIcon.setImageResource(R.drawable.ic_check_24) sendIcon.setImageResource(R.drawable.ic_check_24)
sendIcon.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN) sendIcon.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
@@ -472,6 +474,45 @@ class QuickSendActivity : Activity() {
} }
} }
private fun saveHistory(text: String) {
if (text.trim().isEmpty()) {
return
}
val prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE)
val currentHistory = readHistory()
val nextHistory = linkedSetOf(text)
currentHistory
.filter { it != text }
.take(MAX_HISTORY_ITEMS - 1)
.forEach { nextHistory.add(it) }
prefs.edit()
.putString(KEY_HISTORY, JSON_LIST_PREFIX + JSONArray(nextHistory.toList()).toString())
.apply()
}
private fun readHistory(): List<String> {
val rawValue = getSharedPreferences(PREFS, Context.MODE_PRIVATE).all[KEY_HISTORY]
return when (rawValue) {
is String -> decodeHistoryString(rawValue)
is Set<*> -> rawValue.filterIsInstance<String>()
else -> emptyList()
}
}
private fun decodeHistoryString(value: String): List<String> {
if (!value.startsWith(JSON_LIST_PREFIX)) {
return emptyList()
}
return runCatching {
val array = JSONArray(value.substring(JSON_LIST_PREFIX.length))
List(array.length()) { index -> array.optString(index) }
.filter { it.isNotEmpty() }
}.getOrDefault(emptyList())
}
private fun toggleAppendEnter() { private fun toggleAppendEnter() {
setAppendEnter(!appendEnter) setAppendEnter(!appendEnter)
} }
@@ -529,5 +570,8 @@ class QuickSendActivity : Activity() {
companion object { companion object {
private const val PREFS = "FlutterSharedPreferences" private const val PREFS = "FlutterSharedPreferences"
private const val KEY_APPEND_ENTER = "flutter.append_enter" private const val KEY_APPEND_ENTER = "flutter.append_enter"
private const val KEY_HISTORY = "flutter.send_history"
private const val JSON_LIST_PREFIX = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu!"
private const val MAX_HISTORY_ITEMS = 10
} }
} }

View File

@@ -221,7 +221,7 @@ class _InputSyncPageState extends State<InputSyncPage>
@override @override
void didChangeAppLifecycleState(AppLifecycleState state) { void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) { if (state == AppLifecycleState.resumed) {
unawaited(_syncAppendEnterFromPrefs()); unawaited(_syncSharedPrefsFromDisk());
} }
} }
@@ -239,7 +239,7 @@ class _InputSyncPageState extends State<InputSyncPage>
prefs.getString(_portKey) ?? nativeState.port.toString(); prefs.getString(_portKey) ?? nativeState.port.toString();
clearAfterSend = prefs.getBool(_clearAfterSendKey) ?? false; clearAfterSend = prefs.getBool(_clearAfterSendKey) ?? false;
appendEnter = prefs.getBool(_appendEnterKey) ?? false; appendEnter = prefs.getBool(_appendEnterKey) ?? false;
sendHistory = prefs.getStringList(_historyKey) ?? []; sendHistory = _readHistory(prefs);
connection = nativeState; connection = nativeState;
headerExpanded = !nativeState.connected; headerExpanded = !nativeState.connected;
}); });
@@ -459,17 +459,44 @@ class _InputSyncPageState extends State<InputSyncPage>
}); });
} }
Future<void> _syncAppendEnterFromPrefs() async { Future<void> _syncSharedPrefsFromDisk() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.reload(); await prefs.reload();
final nextAppendEnter = prefs.getBool(_appendEnterKey) ?? false; final nextAppendEnter = prefs.getBool(_appendEnterKey) ?? false;
if (!mounted || nextAppendEnter == appendEnter) { final nextHistory = _readHistory(prefs);
if (!mounted) {
return; return;
} }
setState(() { if (nextAppendEnter != appendEnter ||
appendEnter = nextAppendEnter; !_stringListsEqual(nextHistory, sendHistory)) {
}); setState(() {
appendEnter = nextAppendEnter;
sendHistory = nextHistory;
});
}
}
bool _stringListsEqual(List<String> left, List<String> right) {
if (left.length != right.length) {
return false;
}
for (var index = 0; index < left.length; index += 1) {
if (left[index] != right[index]) {
return false;
}
}
return true;
}
List<String> _readHistory(SharedPreferences prefs) {
try {
return prefs.getStringList(_historyKey) ?? [];
} catch (_) {
return [];
}
} }
void _restoreHistory(String text) { void _restoreHistory(String text) {