mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-24 16:46:35 +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,26 +96,28 @@ object RawKeyboardParser {
|
||||||
context.assets.open("layouts${File.separator}$name").reader().use { it.readText() }
|
context.assets.open("layouts${File.separator}$name").reader().use { it.readText() }
|
||||||
}
|
}
|
||||||
} else context.assets.open("layouts${File.separator}$layoutFileName").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)) {
|
||||||
val florisKeyData = parseJsonString(layoutText)
|
try {
|
||||||
return { params ->
|
val florisKeyData = parseJsonString(layoutText)
|
||||||
florisKeyData.mapTo(mutableListOf()) { row ->
|
return { params ->
|
||||||
row.mapNotNullTo(mutableListOf()) { it.compute(params) }
|
florisKeyData.mapTo(mutableListOf()) { row ->
|
||||||
}
|
row.mapNotNullTo(mutableListOf()) { it.compute(params) }
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val simpleKeyData = parseSimpleString(layoutText)
|
|
||||||
return { params ->
|
|
||||||
simpleKeyData.mapIndexedTo(mutableListOf()) { i, row ->
|
|
||||||
val newRow = row.toMutableList()
|
|
||||||
if (params.mId.isAlphabetKeyboard
|
|
||||||
&& params.mId.mSubtype.keyboardLayoutSetName.endsWith("+")
|
|
||||||
&& "$layoutName+" == params.mId.mSubtype.keyboardLayoutSetName
|
|
||||||
) {
|
|
||||||
params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) }
|
|
||||||
}
|
}
|
||||||
newRow
|
|
||||||
}
|
}
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
}
|
||||||
|
// not a json, or invalid json
|
||||||
|
val simpleKeyData = parseSimpleString(layoutText)
|
||||||
|
return { params ->
|
||||||
|
simpleKeyData.mapIndexedTo(mutableListOf()) { i, row ->
|
||||||
|
val newRow = row.toMutableList()
|
||||||
|
if (params.mId.isAlphabetKeyboard
|
||||||
|
&& params.mId.mSubtype.keyboardLayoutSetName.endsWith("+")
|
||||||
|
&& "$layoutName+" == params.mId.mSubtype.keyboardLayoutSetName
|
||||||
|
) {
|
||||||
|
params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) }
|
||||||
|
}
|
||||||
|
newRow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.CUSTOM_LAYOUT_PREFIX
|
||||||
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||||
import helium314.keyboard.latin.utils.DictionaryInfoUtils
|
import helium314.keyboard.latin.utils.DictionaryInfoUtils
|
||||||
|
import helium314.keyboard.latin.utils.Log
|
||||||
import helium314.keyboard.latin.utils.ToolbarKey
|
import helium314.keyboard.latin.utils.ToolbarKey
|
||||||
import helium314.keyboard.latin.utils.defaultPinnedToolbarPref
|
import helium314.keyboard.latin.utils.defaultPinnedToolbarPref
|
||||||
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
||||||
|
import helium314.keyboard.latin.utils.getCustomLayoutFiles
|
||||||
import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged
|
import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged
|
||||||
import helium314.keyboard.latin.utils.upgradeToolbarPrefs
|
import helium314.keyboard.latin.utils.upgradeToolbarPrefs
|
||||||
import java.io.File
|
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)
|
upgradeToolbarPrefs(prefs)
|
||||||
onCustomLayoutFileListChanged() // just to be sure
|
onCustomLayoutFileListChanged() // just to be sure
|
||||||
prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) }
|
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 libfile by lazy { File(requireContext().filesDir.absolutePath + File.separator + JniUtils.JNI_LIB_IMPORT_FILE_NAME) }
|
||||||
private val backupFilePatterns by lazy { listOf(
|
private val backupFilePatterns by lazy { listOf(
|
||||||
"blacklists/.*\\.txt".toRegex(),
|
"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(),
|
"dicts/.*/.*user\\.dict".toRegex(),
|
||||||
"UserHistoryDictionary.*/UserHistoryDictionary.*\\.(body|header)".toRegex(),
|
"UserHistoryDictionary.*/UserHistoryDictionary.*\\.(body|header)".toRegex(),
|
||||||
"custom_background_image.*".toRegex(),
|
"custom_background_image.*".toRegex(),
|
||||||
|
@ -190,7 +190,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
||||||
?.let { requireContext().assets.open("layouts" + File.separator + it).reader().readText() }
|
?.let { requireContext().assets.open("layouts" + File.separator + it).reader().readText() }
|
||||||
}
|
}
|
||||||
val displayName = layoutName.getStringResourceOrName("layout_", requireContext())
|
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() {
|
private fun showCustomizeFunctionalKeyLayoutsDialog() {
|
||||||
|
@ -216,7 +216,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
||||||
requireContext().assets.open("layouts" + File.separator + defaultLayoutName).reader().readText()
|
requireContext().assets.open("layouts" + File.separator + defaultLayoutName).reader().readText()
|
||||||
}
|
}
|
||||||
val displayName = layoutName.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", requireContext())
|
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")
|
@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) {
|
fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: String, context: Context, onAdded: (String) -> Unit) {
|
||||||
var name = layoutName
|
var name = layoutName
|
||||||
val isJson = checkLayout(layoutContent, context)
|
if (!checkLayout(layoutContent, context))
|
||||||
?: return infoDialog(context, context.getString(R.string.layout_error, "invalid layout file, ${Log.getLog(10).lastOrNull { it.tag == TAG }?.message}"))
|
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)
|
AlertDialog.Builder(context)
|
||||||
.setTitle(R.string.title_layout_name_select)
|
.setTitle(R.string.title_layout_name_select)
|
||||||
|
@ -66,7 +68,7 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: Str
|
||||||
})
|
})
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
// name must be encoded to avoid issues with validity of subtype extra string or file name
|
// 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)
|
val file = getCustomLayoutFile(name, context)
|
||||||
if (file.exists())
|
if (file.exists())
|
||||||
file.delete()
|
file.delete()
|
||||||
|
@ -77,28 +79,23 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: Str
|
||||||
.show()
|
.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()
|
val params = KeyboardParams()
|
||||||
params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET)
|
params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET)
|
||||||
params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT)
|
params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT)
|
||||||
addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL)
|
addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL)
|
||||||
try {
|
try {
|
||||||
val keys = RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
|
val keys = RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
|
||||||
if (!checkKeys(keys))
|
return checkKeys(keys)
|
||||||
return null
|
|
||||||
return true
|
|
||||||
} catch (e: SerializationException) {
|
} catch (e: SerializationException) {
|
||||||
Log.w(TAG, "json parsing error", e)
|
Log.w(TAG, "json parsing error", e)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "json layout parsed, but considered invalid", e)
|
Log.w(TAG, "json layout parsed, but considered invalid", e)
|
||||||
return null
|
return false
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { row -> row.map { it.toKeyParams(params) } }
|
val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { row -> row.map { it.toKeyParams(params) } }
|
||||||
if (!checkKeys(keys))
|
return checkKeys(keys)
|
||||||
return null
|
|
||||||
return false
|
|
||||||
} catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) }
|
} catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) }
|
||||||
if (layoutContent.trimStart().startsWith("[") && layoutContent.trimEnd().endsWith("]")) {
|
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
|
// 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) } }
|
RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } }
|
||||||
} catch (e: Exception) { Log.w(TAG, "json parsing error", e) }
|
} catch (e: Exception) { Log.w(TAG, "json parsing error", e) }
|
||||||
}
|
}
|
||||||
return null
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkKeys(keys: List<List<Key.KeyParams>>): Boolean {
|
fun checkKeys(keys: List<List<Key.KeyParams>>): Boolean {
|
||||||
|
@ -190,27 +187,12 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
||||||
.setView(editText)
|
.setView(editText)
|
||||||
.setPositiveButton(R.string.save) { _, _ ->
|
.setPositiveButton(R.string.save) { _, _ ->
|
||||||
val content = editText.text.toString()
|
val content = editText.text.toString()
|
||||||
val isJson = checkLayout(content, context)
|
if (!checkLayout(content, context)) {
|
||||||
if (isJson == null) {
|
|
||||||
editCustomLayout(layoutName, context, content)
|
editCustomLayout(layoutName, context, content)
|
||||||
infoDialog(context, context.getString(R.string.layout_error, Log.getLog(10).lastOrNull { it.tag == TAG }?.message))
|
infoDialog(context, context.getString(R.string.layout_error, Log.getLog(10).lastOrNull { it.tag == TAG }?.message))
|
||||||
} else {
|
} else {
|
||||||
val wasJson = file.name.substringAfterLast(".") == "json"
|
|
||||||
file.parentFile?.mkdir()
|
file.parentFile?.mkdir()
|
||||||
file.writeText(content)
|
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()
|
onCustomLayoutFileListChanged()
|
||||||
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context)
|
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue