Skip to content
Merged
Show file tree
Hide file tree
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 @@ -267,7 +267,13 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
if (!params.mId.mNumberRowEnabled && params.mId.mNumberRowInSymbols && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) {
// replace first symbols row with number row, but use the labels as popupKeys
val numberRowCopy = numberRow.toMutableList()
numberRowCopy.forEachIndexed { index, keyData -> keyData.popup.symbol = baseKeys[0].getOrNull(index)?.label }
numberRowCopy.forEachIndexed { index, keyData ->
val symbolKey = baseKeys[0].getOrNull(index) ?: return@forEachIndexed
val symbols = mutableListOf<String>()
symbolKey.label.takeIf { it.isNotEmpty() }?.let { symbols.add(it) }
symbolKey.popup.getPopupKeyLabels(params)?.let { symbols.addAll(it) }
if (symbols.isNotEmpty()) keyData.popup.symbols = symbols
}
baseKeys[0] = numberRowCopy
} else if (!params.mId.mNumberRowEnabled && params.mId.isAlphabetKeyboard && !hasBuiltInNumbers()) {
if (baseKeys[0].any { it.popup.main != null || !it.popup.relevant.isNullOrEmpty() } // first row of baseKeys has any layout popup key
Expand All @@ -290,7 +296,11 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
layout.forEachIndexed { i, row ->
val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed
row.forEachIndexed { j, key ->
baseRow.getOrNull(j)?.popup?.symbol = key.label
val baseKey = baseRow.getOrNull(j) ?: return@forEachIndexed
val symbols = mutableListOf<String>()
key.label.takeIf { it.isNotEmpty() }?.let { symbols.add(it) }
key.popup.getPopupKeyLabels(params)?.let { symbols.addAll(it) }
if (symbols.isNotEmpty()) baseKey.popup.symbols = symbols
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ open class PopupSet<T : AbstractKeyData>(
open fun isEmpty(): Boolean = main == null && relevant.isNullOrEmpty()

var numberLabel: String? = null
var symbol: String? = null // maybe list of keys?
var symbols: Collection<String>? = null

fun <U : AbstractKeyData> merge(other: PopupSet<U>?): PopupSet<out AbstractKeyData> {
if (other == null || other.isEmpty()) return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fun createPopupKeysArray(popupSet: PopupSet<*>?, params: KeyboardParams, label:
when (type) {
POPUP_KEYS_NUMBER -> popupSet?.numberLabel?.let { popupKeys.add(it) }
POPUP_KEYS_LAYOUT -> popupSet?.getPopupKeyLabels(params)?.let { popupKeys.addAll(it) }
POPUP_KEYS_SYMBOLS -> popupSet?.symbol?.let { popupKeys.add(it) }
POPUP_KEYS_SYMBOLS -> popupSet?.symbols?.let { popupKeys.addAll(it) }
POPUP_KEYS_LANGUAGE -> params.mLocaleKeyboardInfos.getPopupKeys(label)?.let { popupKeys.addAll(it) }
POPUP_KEYS_LANGUAGE_PRIORITY -> params.mLocaleKeyboardInfos.getPriorityPopupKeys(label)?.let { popupKeys.addAll(it) }
}
Expand Down Expand Up @@ -66,7 +66,7 @@ fun getHintLabel(popupSet: PopupSet<*>?, params: KeyboardParams, label: String):
when (type) {
POPUP_KEYS_NUMBER -> popupSet?.numberLabel?.let { hintLabel = it }
POPUP_KEYS_LAYOUT -> popupSet?.getPopupKeyLabels(params)?.let { hintLabel = it.firstOrNull() }
POPUP_KEYS_SYMBOLS -> popupSet?.symbol?.let { hintLabel = it }
POPUP_KEYS_SYMBOLS -> popupSet?.symbols?.let { hintLabel = it.firstOrNull() }
POPUP_KEYS_LANGUAGE -> params.mLocaleKeyboardInfos.getPopupKeys(label)?.let { hintLabel = it.firstOrNull() }
POPUP_KEYS_LANGUAGE_PRIORITY -> params.mLocaleKeyboardInfos.getPriorityPopupKeys(label)?.let { hintLabel = it.firstOrNull() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ private fun backupLauncher(onError: (String) -> Unit): ManagedActivityResultLaun
zipStream.putNextEntry(ZipEntry(PROTECTED_PREFS_FILE_NAME))
settingsToJsonStream(ctx.protectedPrefs().all, zipStream)
zipStream.closeEntry()
// back up auxiliary SharedPreferences files used by individual features
// (gemini_prefs is intentionally excluded: it is EncryptedSharedPreferences
// whose values are tied to a device-specific master key and contains API keys)
for ((entryName, prefsForBackup) in auxiliaryPrefsToBackUp(ctx)) {
zipStream.putNextEntry(ZipEntry(entryName))
settingsToJsonStream(prefsForBackup.all, zipStream)
zipStream.closeEntry()
}
zipStream.close()
}
} catch (t: Throwable) {
Expand Down Expand Up @@ -198,13 +206,20 @@ private fun restoreLauncher(onError: (String) -> Unit): ManagedActivityResultLau
} else if (entry.name == PREFS_FILE_NAME) {
val prefLines = String(zip.readBytes()).split("\n")
val prefs = ctx.prefs()
prefs.edit { clear() }
prefs.edit(commit = true) { clear() }
readJsonLinesToSettings(prefLines, prefs)
} else if (entry.name == PROTECTED_PREFS_FILE_NAME) {
val prefLines = String(zip.readBytes()).split("\n")
val protectedPrefs = ctx.protectedPrefs()
protectedPrefs.edit { clear() }
protectedPrefs.edit(commit = true) { clear() }
readJsonLinesToSettings(prefLines, protectedPrefs)
} else {
val auxPrefs = auxiliaryPrefsToBackUp(ctx)[entry.name]
if (auxPrefs != null) {
val prefLines = String(zip.readBytes()).split("\n")
auxPrefs.edit(commit = true) { clear() }
readJsonLinesToSettings(prefLines, auxPrefs)
}
}
zip.closeEntry()
entry = zip.nextEntry
Expand Down Expand Up @@ -274,13 +289,31 @@ private fun readJsonLinesToSettings(list: List<String>, prefs: SharedPreferences
"string set settings" -> Json.decodeFromString<Map<String, Set<String>>>(i.next()).forEach { e.putStringSet(it.key, it.value) }
}
}
e.apply()
// commit synchronously so that post-restore actions and a possible process kill
// (e.g. the user closing the app immediately after restore) don't lose data
e.commit()
return true
} catch (e: Exception) {
return false
}
}

/**
* Auxiliary SharedPreferences files (other than the main prefs and protectedPrefs) that
* should be included in backups. The key is the zip entry name to use, and the value
* is the SharedPreferences instance to read from / write back into on restore.
*
* NOTE: This must NOT include EncryptedSharedPreferences (e.g. "gemini_prefs"), because
* those values are encrypted with a device-bound master key and would be unreadable on
* any other device. Plus they typically hold credentials, which we don't want in a plain
* backup zip.
*/
private fun auxiliaryPrefsToBackUp(ctx: android.content.Context): Map<String, SharedPreferences> =
mapOf(
FLOATING_KEYBOARD_PREFS_FILE_NAME
to DeviceProtectedUtils.getSharedPreferences(ctx, "floating_keyboard_prefs"),
)

private fun restoreEntryToDir(zip: ZipInputStream, baseDir: File, entryName: String): Boolean {
val file = File(baseDir, entryName)
val canonicalBase = baseDir.canonicalFile
Expand All @@ -294,6 +327,7 @@ private fun restoreEntryToDir(zip: ZipInputStream, baseDir: File, entryName: Str

private const val PREFS_FILE_NAME = "preferences.json"
private const val PROTECTED_PREFS_FILE_NAME = "protected_preferences.json"
private const val FLOATING_KEYBOARD_PREFS_FILE_NAME = "floating_keyboard_preferences.json"

private val backupFilePatterns by lazy { listOf(
"blacklists${File.separator}.*\\.txt".toRegex(),
Expand Down