mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-15 06:22:45 +00:00
allow customizing symbols layouts
This commit is contained in:
parent
058317b449
commit
14e54686b2
95 changed files with 127 additions and 148 deletions
|
@ -60,10 +60,7 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
|
|||
mParams.mMoreKeyTypes.addAll(sv.mMoreKeyTypes)
|
||||
// add label source only if moreKey type enabled
|
||||
sv.mMoreKeyLabelSources.forEach { if (it in sv.mMoreKeyTypes) mParams.mMoreKeyLabelSources.add(it) }
|
||||
keysInRows = if (mParams.mId.isAlphabetKeyboard && mParams.mId.mSubtype.isCustom)
|
||||
KeyboardParser.parseCustom(mParams, mContext)
|
||||
else
|
||||
KeyboardParser.parseFromAssets(mParams, mContext)
|
||||
keysInRows = KeyboardParser.parseLayout(mParams, mContext)
|
||||
determineAbsoluteValues()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "error parsing layout $id ${id.mElementId}", e)
|
||||
|
|
|
@ -29,9 +29,6 @@ import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.
|
|||
*/
|
||||
class JsonKeyboardParser(private val params: KeyboardParams, private val context: Context) : KeyboardParser(params, context) {
|
||||
|
||||
override fun getLayoutFromAssets(layoutName: String) =
|
||||
context.assets.open("layouts/$layoutName.json").reader().readText()
|
||||
|
||||
override fun parseCoreLayout(layoutContent: String): MutableList<List<KeyData>> {
|
||||
val florisKeyData: List<List<AbstractKeyData>> = florisJsonConfig.decodeFromString(layoutContent)
|
||||
// initially 200 ms parse (debug build on S4 mini)
|
||||
|
|
|
@ -24,11 +24,11 @@ import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace
|
|||
import org.dslul.openboard.inputmethod.latin.define.DebugFlags
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings
|
||||
import org.dslul.openboard.inputmethod.latin.spellcheck.AndroidSpellCheckerService
|
||||
import org.dslul.openboard.inputmethod.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import org.dslul.openboard.inputmethod.latin.utils.InputTypeUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.MORE_KEYS_LAYOUT
|
||||
import org.dslul.openboard.inputmethod.latin.utils.MORE_KEYS_NUMBER
|
||||
import org.dslul.openboard.inputmethod.latin.utils.RunInLocale
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.sumOf
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
@ -48,13 +48,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
|
|||
Key.LABEL_FLAGS_DISABLE_HINT_LABEL // reproduce the no-hints in symbol layouts, todo: add setting
|
||||
else 0
|
||||
|
||||
abstract fun getLayoutFromAssets(layoutName: String): String
|
||||
|
||||
abstract fun parseCoreLayout(layoutContent: String): MutableList<List<KeyData>>
|
||||
|
||||
fun parseLayoutFromAssets(layoutName: String): ArrayList<ArrayList<KeyParams>> =
|
||||
parseLayoutString(getLayoutFromAssets(layoutName))
|
||||
|
||||
// this thing does too much... make it more understandable after everything is implemented
|
||||
fun parseLayoutString(layoutContent: String): ArrayList<ArrayList<KeyParams>> {
|
||||
params.readAttributes(context, null)
|
||||
|
@ -206,11 +201,16 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
|
|||
}
|
||||
|
||||
private fun addSymbolMoreKeys(baseKeys: MutableList<List<KeyData>>) {
|
||||
val symbolsLayoutName = if (ScriptUtils.getScriptFromSpellCheckerLocale(params.mId.locale) == ScriptUtils.SCRIPT_ARABIC)
|
||||
"symbols_arabic"
|
||||
else "symbols"
|
||||
val parser = SimpleKeyboardParser(params, context)
|
||||
parser.parseCoreLayout(parser.getLayoutFromAssets(symbolsLayoutName)).forEachIndexed { i, row ->
|
||||
val layoutName = Settings.readSymbolsLayoutName(context, params.mId.locale)
|
||||
val layout = if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
||||
val file = File(context.filesDir, "layouts${File.separator}$layoutName")
|
||||
val parser = if (layoutName.endsWith("json")) JsonKeyboardParser(params, context)
|
||||
else SimpleKeyboardParser(params, context)
|
||||
parser.parseCoreLayout(file.readText())
|
||||
} else {
|
||||
SimpleKeyboardParser(params, context).parseCoreLayout(context.readAssetsFile("layouts/$layoutName.txt"))
|
||||
}
|
||||
layout.forEachIndexed { i, row ->
|
||||
val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed
|
||||
row.forEachIndexed { j, key ->
|
||||
baseRow.getOrNull(j)?.popup?.symbol = key.label
|
||||
|
@ -771,43 +771,49 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
|
|||
companion object {
|
||||
private const val TAG = "KeyboardParser"
|
||||
|
||||
fun parseCustom(params: KeyboardParams, context: Context): ArrayList<ArrayList<KeyParams>> {
|
||||
val layoutName = params.mId.mSubtype.keyboardLayoutSetName
|
||||
val f = File(context.filesDir, "layouts${File.separator}$layoutName")
|
||||
return if (layoutName.endsWith(".json"))
|
||||
JsonKeyboardParser(params, context).parseLayoutString(f.readText())
|
||||
else
|
||||
SimpleKeyboardParser(params, context).parseLayoutString(f.readText())
|
||||
}
|
||||
|
||||
fun parseFromAssets(params: KeyboardParams, context: Context): ArrayList<ArrayList<KeyParams>> {
|
||||
val id = params.mId
|
||||
val layoutName = params.mId.mSubtype.keyboardLayoutSetName.substringBefore("+")
|
||||
val layoutFileNames = context.assets.list("layouts")!!
|
||||
return when {
|
||||
id.mElementId == KeyboardId.ELEMENT_SYMBOLS && ScriptUtils.getScriptFromSpellCheckerLocale(params.mId.locale) == ScriptUtils.SCRIPT_ARABIC
|
||||
-> SimpleKeyboardParser(params, context).parseLayoutFromAssets("symbols_arabic")
|
||||
id.mElementId == KeyboardId.ELEMENT_SYMBOLS -> SimpleKeyboardParser(params, context).parseLayoutFromAssets("symbols")
|
||||
id.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED
|
||||
-> SimpleKeyboardParser(params, context).parseLayoutFromAssets("symbols_shifted")
|
||||
id.mElementId == KeyboardId.ELEMENT_NUMPAD && context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
-> JsonKeyboardParser(params, context).parseLayoutFromAssets("numpad_landscape")
|
||||
id.mElementId == KeyboardId.ELEMENT_NUMPAD -> JsonKeyboardParser(params, context).parseLayoutFromAssets("numpad")
|
||||
id.mElementId == KeyboardId.ELEMENT_NUMBER -> JsonKeyboardParser(params, context).parseLayoutFromAssets("number")
|
||||
id.mElementId == KeyboardId.ELEMENT_PHONE -> JsonKeyboardParser(params, context).parseLayoutFromAssets("phone")
|
||||
id.mElementId == KeyboardId.ELEMENT_PHONE_SYMBOLS -> JsonKeyboardParser(params, context).parseLayoutFromAssets("phone_symbols")
|
||||
layoutFileNames.contains("$layoutName.json") -> JsonKeyboardParser(params, context).parseLayoutFromAssets(layoutName)
|
||||
layoutFileNames.contains("$layoutName.txt")
|
||||
-> SimpleKeyboardParser(params, context).parseLayoutFromAssets(layoutName)
|
||||
else -> throw IllegalStateException("can't parse layout $layoutName with id $id and elementId ${id.mElementId}")
|
||||
// todo: this is somewhat awkward and could be re-organized
|
||||
// simple and json parser should just parse the core layout
|
||||
// adding extra keys should be done in KeyboardParser
|
||||
fun parseLayout(params: KeyboardParams, context: Context): ArrayList<ArrayList<KeyParams>> {
|
||||
val layoutName = getLayoutFileName(params, context)
|
||||
if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
||||
val file = File(context.filesDir, "layouts${File.separator}$layoutName")
|
||||
val parser = if (layoutName.endsWith("json")) JsonKeyboardParser(params, context)
|
||||
else SimpleKeyboardParser(params, context)
|
||||
return parser.parseLayoutString(file.readText())
|
||||
}
|
||||
val layoutFileNames = context.assets.list("layouts")!!
|
||||
if (layoutFileNames.contains("$layoutName.json")) {
|
||||
return JsonKeyboardParser(params, context).parseLayoutString(context.readAssetsFile("layouts${File.separator}$layoutName.json"))
|
||||
}
|
||||
if (layoutFileNames.contains("$layoutName.txt")) {
|
||||
return SimpleKeyboardParser(params, context).parseLayoutString(context.readAssetsFile("layouts${File.separator}$layoutName.txt"))
|
||||
}
|
||||
throw IllegalStateException("can't parse layout $layoutName with id ${params.mId} and elementId ${params.mId.mElementId}")
|
||||
}
|
||||
|
||||
// todo: layoutInfos should be stored in method.xml (imeSubtypeExtraValue)
|
||||
private fun Context.readAssetsFile(name: String) = assets.open(name).reader().readText()
|
||||
|
||||
private fun getLayoutFileName(params: KeyboardParams, context: Context) = when (params.mId.mElementId) {
|
||||
KeyboardId.ELEMENT_SYMBOLS -> Settings.readSymbolsLayoutName(context, params.mId.locale)
|
||||
KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> Settings.readShiftedSymbolsLayoutName(context)
|
||||
KeyboardId.ELEMENT_NUMPAD -> if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||
"numpad_landscape"
|
||||
else
|
||||
"numpad"
|
||||
KeyboardId.ELEMENT_NUMBER -> "number"
|
||||
KeyboardId.ELEMENT_PHONE -> "phone"
|
||||
KeyboardId.ELEMENT_PHONE_SYMBOLS -> "phone_symbols"
|
||||
else -> params.mId.mSubtype.keyboardLayoutSetName
|
||||
}
|
||||
|
||||
// todo:
|
||||
// layoutInfos should be stored in method.xml (imeSubtypeExtraValue)
|
||||
// or somewhere else... some replacement for keyboard_layout_set xml maybe
|
||||
// move it after old parser is removed
|
||||
// currently only labelFlags are used
|
||||
// touchPositionCorrectionData needs to be loaded, currently always holo is applied in readAttributes
|
||||
// some assets file?
|
||||
// some extended version of locale_key_texts? that would be good, just need to rename the class and file
|
||||
// touchPositionCorrectionData is just the resId, needs to be loaded in parser
|
||||
// currently always holo is applied in readAttributes
|
||||
private fun layoutInfos(params: KeyboardParams): LayoutInfos {
|
||||
val layout = params.mId.mSubtype.keyboardLayoutSetName
|
||||
val language = params.mId.locale.language
|
||||
|
|
|
@ -16,9 +16,6 @@ import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace
|
|||
class SimpleKeyboardParser(private val params: KeyboardParams, private val context: Context) : KeyboardParser(params, context) {
|
||||
private val addExtraKeys = params.mId.mSubtype.keyboardLayoutSetName.endsWith("+")
|
||||
|
||||
override fun getLayoutFromAssets(layoutName: String) =
|
||||
context.assets.open("layouts/$layoutName.txt").reader().readText()
|
||||
|
||||
override fun parseCoreLayout(layoutContent: String): MutableList<List<KeyData>> {
|
||||
val rowStrings = layoutContent.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex())
|
||||
return rowStrings.mapIndexedNotNullTo(mutableListOf()) { i, row ->
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.dslul.openboard.inputmethod.latin.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
|
@ -27,7 +28,9 @@ import org.dslul.openboard.inputmethod.latin.R
|
|||
import org.dslul.openboard.inputmethod.latin.SystemBroadcastReceiver
|
||||
import org.dslul.openboard.inputmethod.latin.common.FileUtils
|
||||
import org.dslul.openboard.inputmethod.latin.settings.SeekBarDialogPreference.ValueProxy
|
||||
import org.dslul.openboard.inputmethod.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import org.dslul.openboard.inputmethod.latin.utils.JniUtils
|
||||
import org.dslul.openboard.inputmethod.latin.utils.editCustomLayout
|
||||
import org.dslul.openboard.inputmethod.latin.utils.infoDialog
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
@ -38,7 +41,6 @@ import java.util.zip.ZipEntry
|
|||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
|
||||
/**
|
||||
* "Advanced" settings sub screen.
|
||||
*
|
||||
|
@ -94,6 +96,20 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
setupKeyLongpressTimeoutSettings()
|
||||
findPreference<Preference>("load_gesture_library")?.setOnPreferenceClickListener { onClickLoadLibrary() }
|
||||
findPreference<Preference>("pref_backup_restore")?.setOnPreferenceClickListener { showBackupRestoreDialog() }
|
||||
|
||||
// todo: this shows !fixedColumnOrder!, which is not a good idea
|
||||
findPreference<Preference>("custom_symbols_layout")?.setOnPreferenceClickListener {
|
||||
val file = "${CUSTOM_LAYOUT_PREFIX}symbols.txt"
|
||||
val oldLayout = if (File(file).exists()) null else context.assets.open("layouts${File.separator}symbols.txt").reader().readText()
|
||||
editCustomLayout(file, context, oldLayout, true)
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("custom_shift_symbols_layout")?.setOnPreferenceClickListener {
|
||||
val file = "${CUSTOM_LAYOUT_PREFIX}shift_symbols.txt"
|
||||
val oldLayout = if (File(file).exists()) null else context.assets.open("layouts${File.separator}symbols_shifted.txt").reader().readText()
|
||||
editCustomLayout(file, context, oldLayout, true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -163,6 +179,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private fun renameToLibfileAndRestart(file: File, checksum: String) {
|
||||
libfile.delete()
|
||||
sharedPreferences.edit().putString("lib_checksum", checksum).commit()
|
||||
|
|
|
@ -14,6 +14,8 @@ import android.content.res.Configuration;
|
|||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.utils.CustomLayoutUtilsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.Log;
|
||||
|
||||
import android.util.TypedValue;
|
||||
|
@ -36,11 +38,13 @@ import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils;
|
|||
import org.dslul.openboard.inputmethod.latin.utils.JniUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.RunInLocale;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.StatsUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.SubtypeSettingsKt;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ToolbarKey;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.ToolbarUtilsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -508,6 +512,28 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
};
|
||||
}
|
||||
|
||||
public static String readSymbolsLayoutName(final Context context, final Locale locale) {
|
||||
String[] layouts = new File(context.getFilesDir(), "layouts").list();
|
||||
if (layouts != null) {
|
||||
for (String name : layouts) {
|
||||
if (name.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX + "symbols"))
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return ScriptUtils.getScriptFromSpellCheckerLocale(locale) == ScriptUtils.SCRIPT_ARABIC ? "symbols_arabic" : "symbols";
|
||||
}
|
||||
|
||||
public static String readShiftedSymbolsLayoutName(final Context context) {
|
||||
String[] layouts = new File(context.getFilesDir(), "layouts").list();
|
||||
if (layouts != null) {
|
||||
for (String name : layouts) {
|
||||
if (name.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX + "shifted_symbols"))
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return "symbols_shifted";
|
||||
}
|
||||
|
||||
public static List<Locale> getSecondaryLocales(final SharedPreferences prefs, final String mainLocaleString) {
|
||||
final String localesString = prefs.getString(PREF_SECONDARY_LOCALES_PREFIX + mainLocaleString.toLowerCase(Locale.ROOT), "");
|
||||
|
||||
|
|
|
@ -133,12 +133,12 @@ fun removeCustomLayoutFile(layoutName: String, context: Context) {
|
|||
getFile(layoutName, context).delete()
|
||||
}
|
||||
|
||||
fun editCustomLayout(layoutName: String, context: Context, startContent: String? = null) {
|
||||
fun editCustomLayout(layoutName: String, context: Context, startContent: String? = null, isSymbols: Boolean = false) {
|
||||
val file = getFile(layoutName, context)
|
||||
val editText = EditText(context).apply {
|
||||
setText(startContent ?: file.readText())
|
||||
}
|
||||
AlertDialog.Builder(context)
|
||||
val builder = AlertDialog.Builder(context)
|
||||
.setTitle(getLayoutDisplayName(layoutName))
|
||||
.setView(editText)
|
||||
.setPositiveButton(R.string.save) { _, _ ->
|
||||
|
@ -149,6 +149,7 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
|||
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, but better be safe
|
||||
file.renameTo(File(file.absolutePath.substringBeforeLast(".") + if (isJson) "json" else "txt"))
|
||||
|
@ -156,7 +157,19 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
|||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
if (isSymbols) {
|
||||
val name = if (layoutName.contains("shift")) context.getString(R.string.shift_symbols) else context.getString(R.string.more_keys_symbols)
|
||||
if (file.exists()) {
|
||||
builder.setNeutralButton(R.string.delete_dict) { _, _ ->
|
||||
confirmDialog(context, context.getString(R.string.delete_layout, name), context.getString(R.string.delete_dict)) {
|
||||
file.delete()
|
||||
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.setTitle(name)
|
||||
}
|
||||
builder.show()
|
||||
}
|
||||
|
||||
private fun encodeBase36(string: String): String = BigInteger(string.toByteArray()).toString(36)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue