mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-24 08:36:26 +00:00
don't use json/txt file endings for custom layouts
just determine json or simple when parsing fixes #1034
This commit is contained in:
parent
a1a7489856
commit
44eb296d38
4 changed files with 75 additions and 50 deletions
|
@ -96,14 +96,17 @@ object RawKeyboardParser {
|
|||
context.assets.open("layouts${File.separator}$name").reader().use { it.readText() }
|
||||
}
|
||||
} else context.assets.open("layouts${File.separator}$layoutFileName").reader().use { it.readText() }
|
||||
if (layoutFileName.endsWith(".json")) {
|
||||
if (layoutFileName.endsWith(".json") || layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
||||
try {
|
||||
val florisKeyData = parseJsonString(layoutText)
|
||||
return { params ->
|
||||
florisKeyData.mapTo(mutableListOf()) { row ->
|
||||
row.mapNotNullTo(mutableListOf()) { it.compute(params) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
// not a json, or invalid json
|
||||
val simpleKeyData = parseSimpleString(layoutText)
|
||||
return { params ->
|
||||
simpleKeyData.mapIndexedTo(mutableListOf()) { i, row ->
|
||||
|
@ -118,7 +121,6 @@ object RawKeyboardParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLayoutName(params: KeyboardParams, context: Context) = when (params.mId.mElementId) {
|
||||
KeyboardId.ELEMENT_SYMBOLS -> if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS
|
||||
|
|
|
@ -11,9 +11,11 @@ import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX
|
|||
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||
import helium314.keyboard.latin.utils.DictionaryInfoUtils
|
||||
import helium314.keyboard.latin.utils.Log
|
||||
import helium314.keyboard.latin.utils.ToolbarKey
|
||||
import helium314.keyboard.latin.utils.defaultPinnedToolbarPref
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutFiles
|
||||
import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged
|
||||
import helium314.keyboard.latin.utils.upgradeToolbarPrefs
|
||||
import java.io.File
|
||||
|
@ -104,6 +106,45 @@ fun checkVersionUpgrade(context: Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion <= 2201) {
|
||||
val additionalSubtypeString = Settings.readPrefAdditionalSubtypes(prefs, context.resources)
|
||||
if (additionalSubtypeString.contains(".")) { // means there are custom layouts
|
||||
val subtypeStrings = additionalSubtypeString.split(";")
|
||||
val newSubtypeStrings = subtypeStrings.mapNotNull {
|
||||
val split = it.split(":").toMutableList()
|
||||
Log.i("test", "0: $it")
|
||||
if (split.size < 2) return@mapNotNull null // should never happen
|
||||
val oldName = split[1]
|
||||
val newName = oldName.substringBeforeLast(".") + "."
|
||||
if (oldName == newName) return@mapNotNull split.joinToString(":") // should never happen
|
||||
val oldFile = getCustomLayoutFile(oldName, context)
|
||||
val newFile = getCustomLayoutFile(newName, context)
|
||||
Log.i("test", "1")
|
||||
if (!oldFile.exists()) return@mapNotNull null // should never happen
|
||||
Log.i("test", "2")
|
||||
if (newFile.exists()) newFile.delete() // should never happen
|
||||
Log.i("test", "3")
|
||||
oldFile.renameTo(newFile)
|
||||
val enabledSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, "")!!
|
||||
if (enabledSubtypes.contains(oldName))
|
||||
prefs.edit { putString(Settings.PREF_ENABLED_SUBTYPES, enabledSubtypes.replace(oldName, newName)) }
|
||||
val selectedSubtype = prefs.getString(Settings.PREF_SELECTED_SUBTYPE, "")!!
|
||||
if (selectedSubtype.contains(oldName))
|
||||
prefs.edit { putString(Settings.PREF_SELECTED_SUBTYPE, selectedSubtype.replace(oldName, newName)) }
|
||||
split[1] = newName
|
||||
split.joinToString(":")
|
||||
}
|
||||
Settings.writePrefAdditionalSubtypes(prefs, newSubtypeStrings.joinToString(";"))
|
||||
}
|
||||
// rename other custom layouts
|
||||
onCustomLayoutFileListChanged()
|
||||
getCustomLayoutFiles(context).forEach {
|
||||
val newFile = getCustomLayoutFile(it.name.substringBeforeLast(".") + ".", context)
|
||||
if (newFile.name == it.name) return@forEach
|
||||
if (newFile.exists()) newFile.delete() // should never happen
|
||||
it.renameTo(newFile)
|
||||
}
|
||||
}
|
||||
upgradeToolbarPrefs(prefs)
|
||||
onCustomLayoutFileListChanged() // just to be sure
|
||||
prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) }
|
||||
|
|
|
@ -87,7 +87,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
private val libfile by lazy { File(requireContext().filesDir.absolutePath + File.separator + JniUtils.JNI_LIB_IMPORT_FILE_NAME) }
|
||||
private val backupFilePatterns by lazy { listOf(
|
||||
"blacklists/.*\\.txt".toRegex(),
|
||||
"layouts/.*.(txt|json)".toRegex(),
|
||||
"layouts/$CUSTOM_LAYOUT_PREFIX+\\..{0,4}".toRegex(), // can't expect a period at the end, as this would break restoring older backups
|
||||
"dicts/.*/.*user\\.dict".toRegex(),
|
||||
"UserHistoryDictionary.*/UserHistoryDictionary.*\\.(body|header)".toRegex(),
|
||||
"custom_background_image.*".toRegex(),
|
||||
|
@ -190,7 +190,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
?.let { requireContext().assets.open("layouts" + File.separator + it).reader().readText() }
|
||||
}
|
||||
val displayName = layoutName.getStringResourceOrName("layout_", requireContext())
|
||||
editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layoutName.txt", requireContext(), originalLayout, displayName)
|
||||
editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layoutName.", requireContext(), originalLayout, displayName)
|
||||
}
|
||||
|
||||
private fun showCustomizeFunctionalKeyLayoutsDialog() {
|
||||
|
@ -216,7 +216,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
requireContext().assets.open("layouts" + File.separator + defaultLayoutName).reader().readText()
|
||||
}
|
||||
val displayName = layoutName.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", requireContext())
|
||||
editCustomLayout(customLayoutName ?: "$layoutName.json", requireContext(), originalLayout, displayName)
|
||||
editCustomLayout(customLayoutName ?: "$layoutName.", requireContext(), originalLayout, displayName)
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
|
|
|
@ -52,8 +52,10 @@ fun loadCustomLayout(uri: Uri?, languageTag: String, context: Context, onAdded:
|
|||
|
||||
fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: String, context: Context, onAdded: (String) -> Unit) {
|
||||
var name = layoutName
|
||||
val isJson = checkLayout(layoutContent, context)
|
||||
?: return infoDialog(context, context.getString(R.string.layout_error, "invalid layout file, ${Log.getLog(10).lastOrNull { it.tag == TAG }?.message}"))
|
||||
if (!checkLayout(layoutContent, context))
|
||||
return infoDialog(context, context.getString(R.string.layout_error, "invalid layout file, ${Log.getLog(10).lastOrNull { it.tag == TAG }?.message}"))
|
||||
// val isJson = checkLayout(layoutContent, context)
|
||||
// ?: return infoDialog(context, context.getString(R.string.layout_error, "invalid layout file, ${Log.getLog(10).lastOrNull { it.tag == TAG }?.message}"))
|
||||
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(R.string.title_layout_name_select)
|
||||
|
@ -66,7 +68,7 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: Str
|
|||
})
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
// name must be encoded to avoid issues with validity of subtype extra string or file name
|
||||
name = "$CUSTOM_LAYOUT_PREFIX${languageTag}.${encodeBase36(name)}.${if (isJson) "json" else "txt"}"
|
||||
name = "$CUSTOM_LAYOUT_PREFIX${languageTag}.${encodeBase36(name)}."
|
||||
val file = getCustomLayoutFile(name, context)
|
||||
if (file.exists())
|
||||
file.delete()
|
||||
|
@ -77,28 +79,23 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: Str
|
|||
.show()
|
||||
}
|
||||
|
||||
/** @return true if json, false if simple, null if invalid */
|
||||
private fun checkLayout(layoutContent: String, context: Context): Boolean? {
|
||||
private fun checkLayout(layoutContent: String, context: Context): Boolean {
|
||||
val params = KeyboardParams()
|
||||
params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET)
|
||||
params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT)
|
||||
addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL)
|
||||
try {
|
||||
val keys = RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
|
||||
if (!checkKeys(keys))
|
||||
return null
|
||||
return true
|
||||
return checkKeys(keys)
|
||||
} catch (e: SerializationException) {
|
||||
Log.w(TAG, "json parsing error", e)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "json layout parsed, but considered invalid", e)
|
||||
return null
|
||||
return false
|
||||
}
|
||||
try {
|
||||
val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { row -> row.map { it.toKeyParams(params) } }
|
||||
if (!checkKeys(keys))
|
||||
return null
|
||||
return false
|
||||
return checkKeys(keys)
|
||||
} catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) }
|
||||
if (layoutContent.trimStart().startsWith("[") && layoutContent.trimEnd().endsWith("]")) {
|
||||
// layout can't be loaded, assume it's json -> load json layout again because the error message shown to the user is from the most recent error
|
||||
|
@ -106,7 +103,7 @@ private fun checkLayout(layoutContent: String, context: Context): Boolean? {
|
|||
RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
|
||||
} catch (e: Exception) { Log.w(TAG, "json parsing error", e) }
|
||||
}
|
||||
return null
|
||||
return false
|
||||
}
|
||||
|
||||
fun checkKeys(keys: List<List<Key.KeyParams>>): Boolean {
|
||||
|
@ -190,27 +187,12 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
|||
.setView(editText)
|
||||
.setPositiveButton(R.string.save) { _, _ ->
|
||||
val content = editText.text.toString()
|
||||
val isJson = checkLayout(content, context)
|
||||
if (isJson == null) {
|
||||
if (!checkLayout(content, context)) {
|
||||
editCustomLayout(layoutName, context, content)
|
||||
infoDialog(context, context.getString(R.string.layout_error, Log.getLog(10).lastOrNull { it.tag == TAG }?.message))
|
||||
} else {
|
||||
val wasJson = file.name.substringAfterLast(".") == "json"
|
||||
file.parentFile?.mkdir()
|
||||
file.writeText(content)
|
||||
if (isJson != wasJson) {
|
||||
// unlikely to be needed
|
||||
// actually does not work properly (need to restart the app)
|
||||
// todo: can we just remove the json and txt endings and determine type using trc/catch?
|
||||
// now we cache the layouts, so a bit slower reading should be fine
|
||||
val newEnding = if (isJson) ".json" else ".txt"
|
||||
file.renameTo(File(file.absolutePath.substringBeforeLast(".") + newEnding))
|
||||
val newLayoutName = layoutName.substringBeforeLast(".") + newEnding
|
||||
val prefs = DeviceProtectedUtils.getSharedPreferences(context)
|
||||
val subtypesString = Settings.readPrefAdditionalSubtypes(prefs, context.resources)
|
||||
val newSubtypesString = subtypesString.replace(layoutName, newLayoutName)
|
||||
Settings.writePrefAdditionalSubtypes(prefs, newSubtypesString)
|
||||
}
|
||||
onCustomLayoutFileListChanged()
|
||||
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue