mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-16 04:12:02 +00:00
add setting for customizing functional key layouts
This commit is contained in:
parent
17aa321fbd
commit
0459971e3a
10 changed files with 150 additions and 61 deletions
|
@ -197,8 +197,8 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
|
|||
val functionalKeysBottom = allFunctionalKeys.lastOrNull() ?: return
|
||||
if (!params.mId.isAlphaOrSymbolKeyboard || functionalKeysBottom.isEmpty() || functionalKeysBottom.any { it.isKeyPlaceholder() })
|
||||
return
|
||||
if (true /* Settings.getInstance().current.mSingleFunctionalLayout */) { // todo with the customizable functional layout
|
||||
// remove unwanted keys (emoji, numpad, language switch)
|
||||
if (!Settings.getInstance().current.mHasCustomFunctionalLayout) {
|
||||
// remove keys that should only exist on specific layouts or depend on setting (emoji, numpad, language switch)
|
||||
if (!Settings.getInstance().current.mShowsEmojiKey || !params.mId.isAlphabetKeyboard)
|
||||
functionalKeysBottom.removeFirst { it.label == KeyLabel.EMOJI }
|
||||
if (!Settings.getInstance().current.isLanguageSwitchKeyEnabled || !params.mId.isAlphabetKeyboard)
|
||||
|
@ -314,7 +314,3 @@ const val LAYOUT_NUMPAD_LANDSCAPE = "numpad_landscape"
|
|||
const val LAYOUT_NUMBER = "number"
|
||||
const val LAYOUT_PHONE = "phone"
|
||||
const val LAYOUT_PHONE_SYMBOLS = "phone_symbols"
|
||||
const val FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED = "functional_keys_symbols_shifted"
|
||||
const val FUNCTIONAL_LAYOUT_SYMBOLS = "functional_keys_symbols"
|
||||
const val FUNCTIONAL_LAYOUT = "functional_keys"
|
||||
const val FUNCTIONAL_LAYOUT_TABLET = "functional_keys_tablet"
|
||||
|
|
|
@ -22,8 +22,9 @@ import helium314.keyboard.latin.settings.Settings
|
|||
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import helium314.keyboard.latin.utils.ScriptUtils
|
||||
import helium314.keyboard.latin.utils.ScriptUtils.script
|
||||
import helium314.keyboard.latin.utils.getCustomFunctionalLayoutName
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutsDir
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutFiles
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
|
@ -35,13 +36,12 @@ object RawKeyboardParser {
|
|||
val symbolAndNumberLayouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC,
|
||||
LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS)
|
||||
|
||||
// todo: cache is by layout name, this is inefficient for functional keys by default
|
||||
fun clearCache() = rawLayoutCache.clear()
|
||||
|
||||
fun parseLayout(params: KeyboardParams, context: Context, isFunctional: Boolean = false): MutableList<MutableList<KeyData>> {
|
||||
val layoutName = if (isFunctional) {
|
||||
if (!params.mId.isAlphaOrSymbolKeyboard) return mutableListOf(mutableListOf())
|
||||
else getFunctionalLayoutName(params)
|
||||
else getFunctionalLayoutName(params, context)
|
||||
} else {
|
||||
getLayoutName(params, context)
|
||||
}
|
||||
|
@ -121,43 +121,24 @@ object RawKeyboardParser {
|
|||
else -> params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+")
|
||||
}
|
||||
|
||||
// todo (later, see also keyboardParser): use Settings.getInstance().current.mSingleFunctionalLayout
|
||||
private fun getFunctionalLayoutName(params: KeyboardParams) = when (params.mId.mElementId) {
|
||||
KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED
|
||||
KeyboardId.ELEMENT_SYMBOLS -> FUNCTIONAL_LAYOUT_SYMBOLS
|
||||
else -> if (Settings.getInstance().isTablet) FUNCTIONAL_LAYOUT_TABLET else FUNCTIONAL_LAYOUT
|
||||
private fun getFunctionalLayoutName(params: KeyboardParams, context: Context): String {
|
||||
if (Settings.getInstance().current.mHasCustomFunctionalLayout) {
|
||||
getCustomFunctionalLayoutName(params.mId.mElementId, params.mId.mSubtype.rawSubtype, context)
|
||||
?.let { return it }
|
||||
}
|
||||
return if (Settings.getInstance().isTablet) "functional_keys_tablet" else "functional_keys"
|
||||
}
|
||||
|
||||
/** returns the file name matching the layout name, making sure the file exists (falling back to qwerty.txt) */
|
||||
private fun getLayoutFileName(layoutName: String, context: Context): String {
|
||||
val customFiles = getCustomLayoutsDir(context).list()
|
||||
val customFiles = getCustomLayoutFiles(context).map { it.name }
|
||||
if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
||||
return if (customFiles?.contains(layoutName) == true) layoutName
|
||||
else "qwerty.txt" // fallback
|
||||
return customFiles.firstOrNull { it.startsWith(layoutName)}
|
||||
?: if (layoutName.contains("functional")) "functional_keys.json" else "qwerty.txt" // fallback to defaults
|
||||
}
|
||||
val assetsFiles by lazy { context.assets.list("layouts")!! }
|
||||
if (layoutName.startsWith("functional")) {
|
||||
// return custom match if we have one
|
||||
val customMatch = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") }
|
||||
if (customMatch != null) return customMatch
|
||||
if (layoutName == FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) {
|
||||
// no custom symbols shifted layout, try custom symbols layout
|
||||
val customSymbols = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_SYMBOLS.") }
|
||||
if (customSymbols != null) return customSymbols
|
||||
}
|
||||
// no custom symbols layout, try custom functional layout
|
||||
if (Settings.getInstance().isTablet) {
|
||||
val customTablet = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_TABLET.") }
|
||||
if (customTablet != null) return customTablet
|
||||
}
|
||||
val customFunctional = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT.") }
|
||||
if (customFunctional != null) return customFunctional
|
||||
// no custom functional layout, use the default functional layout
|
||||
return if (Settings.getInstance().isTablet) "$FUNCTIONAL_LAYOUT_TABLET.json"
|
||||
else "$FUNCTIONAL_LAYOUT.json"
|
||||
}
|
||||
return if (layoutName in symbolAndNumberLayouts) {
|
||||
customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")}
|
||||
customFiles.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")}
|
||||
?: assetsFiles.first { it.startsWith(layoutName) }
|
||||
} else {
|
||||
// can't be custom layout, so it must be in assets
|
||||
|
|
|
@ -11,7 +11,8 @@ 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.getCustomLayoutsDir
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
||||
import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged
|
||||
import helium314.keyboard.latin.utils.upgradeToolbarPrefs
|
||||
import java.io.File
|
||||
|
||||
|
@ -51,10 +52,9 @@ fun checkVersionUpgrade(context: Context) {
|
|||
if (oldVersion == 0) // new install or restoring settings from old app name
|
||||
upgradesWhenComingFromOldAppName(context)
|
||||
if (oldVersion <= 1000) { // upgrade old custom layouts name
|
||||
val layoutsDir = getCustomLayoutsDir(context)
|
||||
val oldShiftSymbolsFile = File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}shift_symbols")
|
||||
val oldShiftSymbolsFile = getCustomLayoutFile("${CUSTOM_LAYOUT_PREFIX}shift_symbols", context)
|
||||
if (oldShiftSymbolsFile.exists()) {
|
||||
oldShiftSymbolsFile.renameTo(File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}symbols_shifted"))
|
||||
oldShiftSymbolsFile.renameTo(getCustomLayoutFile("${CUSTOM_LAYOUT_PREFIX}symbols_shifted", context))
|
||||
}
|
||||
|
||||
// rename subtype setting, and clean old subtypes that might remain in some cases
|
||||
|
@ -73,6 +73,7 @@ fun checkVersionUpgrade(context: Context) {
|
|||
putString(Settings.PREF_SELECTED_SUBTYPE, selectedSubtype)
|
||||
}
|
||||
}
|
||||
onCustomLayoutFileListChanged() // just to be sure
|
||||
prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) }
|
||||
}
|
||||
|
||||
|
@ -80,9 +81,8 @@ fun checkVersionUpgrade(context: Context) {
|
|||
private fun upgradesWhenComingFromOldAppName(context: Context) {
|
||||
// move layout files
|
||||
try {
|
||||
val layoutsDir = getCustomLayoutsDir(context)
|
||||
File(context.filesDir, "layouts").listFiles()?.forEach {
|
||||
it.copyTo(File(layoutsDir, it.name), true)
|
||||
it.copyTo(getCustomLayoutFile(it.name, context), true)
|
||||
it.delete()
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
|
|
|
@ -41,12 +41,15 @@ import helium314.keyboard.latin.common.FileUtils
|
|||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||
import helium314.keyboard.latin.settings.SeekBarDialogPreference.ValueProxy
|
||||
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils
|
||||
import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_NORMAL
|
||||
import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS
|
||||
import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED
|
||||
import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX
|
||||
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||
import helium314.keyboard.latin.utils.ExecutorUtils
|
||||
import helium314.keyboard.latin.utils.JniUtils
|
||||
import helium314.keyboard.latin.utils.editCustomLayout
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutsDir
|
||||
import helium314.keyboard.latin.utils.getCustomLayoutFiles
|
||||
import helium314.keyboard.latin.utils.getStringResourceOrName
|
||||
import helium314.keyboard.latin.utils.infoDialog
|
||||
import helium314.keyboard.latin.utils.reloadEnabledSubtypes
|
||||
|
@ -127,7 +130,11 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
findPreference<Preference>("backup_restore")?.setOnPreferenceClickListener { showBackupRestoreDialog() }
|
||||
|
||||
findPreference<Preference>("custom_symbols_number_layouts")?.setOnPreferenceClickListener {
|
||||
showCustomizeLayoutsDialog()
|
||||
showCustomizeSymbolNumberLayoutsDialog()
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("custom_functional_key_layouts")?.setOnPreferenceClickListener {
|
||||
showCustomizeFunctionalKeyLayoutsDialog()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +152,7 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun showCustomizeLayoutsDialog() {
|
||||
private fun showCustomizeSymbolNumberLayoutsDialog() {
|
||||
val layoutNames = RawKeyboardParser.symbolAndNumberLayouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray()
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.customize_symbols_number_layouts)
|
||||
|
@ -158,8 +165,8 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
}
|
||||
|
||||
private fun customizeSymbolNumberLayout(layoutName: String) {
|
||||
val customLayoutName = getCustomLayoutsDir(requireContext()).list()
|
||||
?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") }
|
||||
val customLayoutName = getCustomLayoutFiles(requireContext()).map { it.name }
|
||||
.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") }
|
||||
val originalLayout = if (customLayoutName != null) null
|
||||
else {
|
||||
requireContext().assets.list("layouts")?.firstOrNull { it.startsWith("$layoutName.") }
|
||||
|
@ -169,6 +176,32 @@ class AdvancedSettingsFragment : SubScreenFragment() {
|
|||
editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layoutName.txt", requireContext(), originalLayout, displayName)
|
||||
}
|
||||
|
||||
private fun showCustomizeFunctionalKeyLayoutsDialog() {
|
||||
val list = listOf(CUSTOM_FUNCTIONAL_LAYOUT_NORMAL, CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS, CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED)
|
||||
.map { it.substringBeforeLast(".") }
|
||||
val layoutNames = list.map { it.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", requireContext()) }.toTypedArray()
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.customize_functional_key_layouts)
|
||||
.setItems(layoutNames) { di, i ->
|
||||
di.dismiss()
|
||||
customizeFunctionalKeysLayout(list[i])
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun customizeFunctionalKeysLayout(layoutName: String) {
|
||||
val customLayoutName = getCustomLayoutFiles(requireContext()).map { it.name }
|
||||
.firstOrNull { it.startsWith("$layoutName.") }
|
||||
val originalLayout = if (customLayoutName != null) null
|
||||
else {
|
||||
val defaultLayoutName = if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json"
|
||||
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)
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private fun onClickLoadLibrary(): Boolean {
|
||||
// get architecture for telling user which file to use
|
||||
|
|
|
@ -25,6 +25,7 @@ import helium314.keyboard.latin.R;
|
|||
import helium314.keyboard.latin.RichInputMethodManager;
|
||||
import helium314.keyboard.latin.common.Colors;
|
||||
import helium314.keyboard.latin.permissions.PermissionsUtil;
|
||||
import helium314.keyboard.latin.utils.CustomLayoutUtilsKt;
|
||||
import helium314.keyboard.latin.utils.InputTypeUtils;
|
||||
import helium314.keyboard.latin.utils.Log;
|
||||
import helium314.keyboard.latin.utils.PopupKeysUtilsKt;
|
||||
|
@ -122,6 +123,7 @@ public class SettingsValues {
|
|||
public final SettingsValuesForSuggestion mSettingsValuesForSuggestion;
|
||||
public final boolean mIncognitoModeEnabled;
|
||||
public final boolean mLongPressSymbolsForNumpad;
|
||||
public final boolean mHasCustomFunctionalLayout;
|
||||
|
||||
// User-defined colors
|
||||
public final Colors mColors;
|
||||
|
@ -237,6 +239,7 @@ public class SettingsValues {
|
|||
mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled);
|
||||
mBottomPaddingScale = prefs.getFloat(Settings.PREF_BOTTOM_PADDING_SCALE, DEFAULT_SIZE_SCALE);
|
||||
mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, false);
|
||||
mHasCustomFunctionalLayout = CustomLayoutUtilsKt.hasCustomFunctionalLayout(selectedSubtype, context);
|
||||
}
|
||||
|
||||
public boolean isApplicationSpecifiedCompletionsOn() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.text.InputType
|
||||
import android.view.inputmethod.InputMethodSubtype
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
|
@ -17,6 +18,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL
|
|||
import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
|
||||
import helium314.keyboard.latin.R
|
||||
import helium314.keyboard.latin.common.Constants
|
||||
import helium314.keyboard.latin.common.FileUtils
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
@ -112,7 +114,7 @@ private fun checkKeys(keys: List<List<Key.KeyParams>>): Boolean {
|
|||
Log.w(TAG, "too many keys in one row")
|
||||
return false
|
||||
}
|
||||
if (keys.any { row -> row.any { ((it.mLabel?.length ?: 0) > 6) } }) {
|
||||
if (keys.any { row -> row.any { ((it.mLabel?.length ?: 0) > 20) } }) {
|
||||
Log.w(TAG, "too long text on key")
|
||||
return false
|
||||
}
|
||||
|
@ -127,10 +129,24 @@ private fun checkKeys(keys: List<List<Key.KeyParams>>): Boolean {
|
|||
return true
|
||||
}
|
||||
|
||||
/** don't rename or delete the file without calling [onCustomLayoutFileListChanged] */
|
||||
fun getCustomLayoutFile(layoutName: String, context: Context) =
|
||||
File(getCustomLayoutsDir(context), layoutName)
|
||||
|
||||
fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts")
|
||||
// cache to avoid frequently listing files
|
||||
/** don't rename or delete files without calling [onCustomLayoutFileListChanged] */
|
||||
fun getCustomLayoutFiles(context: Context): List<File> {
|
||||
customLayouts?.let { return it }
|
||||
val layouts = getCustomLayoutsDir(context).listFiles()?.toList() ?: emptyList()
|
||||
customLayouts = layouts
|
||||
return layouts
|
||||
}
|
||||
|
||||
fun onCustomLayoutFileListChanged() {
|
||||
customLayouts = null
|
||||
}
|
||||
|
||||
private fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts")
|
||||
|
||||
// undo the name changes in loadCustomLayout when clicking ok
|
||||
fun getLayoutDisplayName(layoutName: String) =
|
||||
|
@ -164,6 +180,7 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
|||
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"))
|
||||
onCustomLayoutFileListChanged()
|
||||
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context)
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +190,7 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
|||
builder.setNeutralButton(R.string.delete) { _, _ ->
|
||||
confirmDialog(context, context.getString(R.string.delete_layout, displayName), context.getString(R.string.delete)) {
|
||||
file.delete()
|
||||
onCustomLayoutFileListChanged()
|
||||
KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context)
|
||||
}
|
||||
}
|
||||
|
@ -182,6 +200,45 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String?
|
|||
builder.show()
|
||||
}
|
||||
|
||||
fun hasCustomFunctionalLayout(subtype: InputMethodSubtype, context: Context): Boolean {
|
||||
val anyCustomFunctionalLayout = getCustomFunctionalLayoutName(KeyboardId.ELEMENT_ALPHABET, subtype, context)
|
||||
?: getCustomFunctionalLayoutName(KeyboardId.ELEMENT_SYMBOLS, subtype, context)
|
||||
?: getCustomFunctionalLayoutName(KeyboardId.ELEMENT_SYMBOLS_SHIFTED, subtype, context)
|
||||
return anyCustomFunctionalLayout != null
|
||||
}
|
||||
|
||||
fun getCustomFunctionalLayoutName(elementId: Int, subtype: InputMethodSubtype, context: Context): String? {
|
||||
val customFunctionalLayoutNames = getCustomLayoutFiles(context).filter { it.name.contains("functional") }.map { it.name.substringBeforeLast(".") + "." }
|
||||
if (customFunctionalLayoutNames.isEmpty()) return null
|
||||
val languageTag = subtype.locale().toLanguageTag()
|
||||
val mainLayoutName = subtype.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: "qwerty"
|
||||
|
||||
if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
|
||||
findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) }, mainLayoutName, languageTag)
|
||||
?.let { return it }
|
||||
}
|
||||
if (elementId == KeyboardId.ELEMENT_SYMBOLS) {
|
||||
findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS) }, mainLayoutName, languageTag)
|
||||
?.let { return it }
|
||||
}
|
||||
return findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_NORMAL) }, mainLayoutName, languageTag)
|
||||
}
|
||||
|
||||
// todo (when adding custom layouts per locale or main layout): adjust mainLayoutName for custom layouts?
|
||||
// remove language tag and file ending (currently name is e.g. custom.en-US.abcdfdsg3.json, and we could use abcdfdsg3 only)
|
||||
// this way, custom layouts with same name could use same custom functional layouts
|
||||
// currently there is no way to set the language tag or main layout name, so changes don't break backwards compatibility
|
||||
private fun findMatchingLayout(layoutNames: List<String>, mainLayoutName: String, languageTag: String): String? {
|
||||
// first find layout with matching locale and main layout
|
||||
return layoutNames.firstOrNull { it.endsWith(".$languageTag.$mainLayoutName.") }
|
||||
// then find matching main layout
|
||||
?: layoutNames.firstOrNull { it.endsWith(".$mainLayoutName.") }
|
||||
// then find matching language
|
||||
?: layoutNames.firstOrNull { it.endsWith(".$languageTag.") }
|
||||
// then find "normal" functional layout (make use of the '.' separator
|
||||
?: layoutNames.firstOrNull { it.count { it == '.' } == 2 }
|
||||
}
|
||||
|
||||
private fun encodeBase36(string: String): String = BigInteger(string.toByteArray()).toString(36)
|
||||
|
||||
private fun decodeBase36(string: String) = BigInteger(string, 36).toByteArray().decodeToString()
|
||||
|
@ -189,3 +246,8 @@ private fun decodeBase36(string: String) = BigInteger(string, 36).toByteArray().
|
|||
// this goes into prefs and file names, so do not change!
|
||||
const val CUSTOM_LAYOUT_PREFIX = "custom."
|
||||
private const val TAG = "CustomLayoutUtils"
|
||||
private var customLayouts: List<File>? = null
|
||||
|
||||
const val CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED = "${CUSTOM_LAYOUT_PREFIX}functional_keys_symbols_shifted."
|
||||
const val CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS = "${CUSTOM_LAYOUT_PREFIX}functional_keys_symbols."
|
||||
const val CUSTOM_FUNCTIONAL_LAYOUT_NORMAL = "${CUSTOM_LAYOUT_PREFIX}functional_keys."
|
||||
|
|
|
@ -245,12 +245,12 @@ private fun loadResourceSubtypes(resources: Resources) {
|
|||
private fun removeInvalidCustomSubtypes(context: Context) {
|
||||
val prefs = DeviceProtectedUtils.getSharedPreferences(context)
|
||||
val additionalSubtypes = Settings.readPrefAdditionalSubtypes(prefs, context.resources).split(";")
|
||||
val customSubtypeFiles by lazy { getCustomLayoutsDir(context).list() }
|
||||
val customSubtypeFiles by lazy { getCustomLayoutFiles(context).map { it.name } }
|
||||
val subtypesToRemove = mutableListOf<String>()
|
||||
additionalSubtypes.forEach {
|
||||
val name = it.substringAfter(":").substringBefore(":")
|
||||
if (!name.startsWith(CUSTOM_LAYOUT_PREFIX)) return@forEach
|
||||
if (customSubtypeFiles?.contains(name) != true)
|
||||
if (name !in customSubtypeFiles)
|
||||
subtypesToRemove.add(it)
|
||||
}
|
||||
if (subtypesToRemove.isEmpty()) return
|
||||
|
|
|
@ -462,6 +462,14 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM
|
|||
<string name="edit_layout">Tap to edit raw layout</string>
|
||||
<!-- Title for customizing symbols or number layouts -->
|
||||
<string name="customize_symbols_number_layouts">Customize symbols and number layouts</string>
|
||||
<!-- Title for customizing functional key layouts -->
|
||||
<string name="customize_functional_key_layouts">Customize functional key layouts</string>
|
||||
<!-- Name for functional keys layout -->
|
||||
<string name="layout_functional_keys" tools:keep="@string/layout_functional_keys">Functional keys</string>
|
||||
<!-- Name for functional keys layout when showing symbols layout -->
|
||||
<string name="layout_functional_keys_symbols" tools:keep="@string/layout_functional_keys_symbols">Functional keys (Symbols)</string>
|
||||
<!-- Name for functional keys layout when showing more symbols layout -->
|
||||
<string name="layout_functional_keys_symbols_shifted" tools:keep="@string/layout_functional_keys_symbols_shifted">Functional keys (More symbols)</string>
|
||||
<!-- Name for symbols layout -->
|
||||
<string name="layout_symbols" tools:keep="@string/layout_symbols">Symbols</string>
|
||||
<!-- Name for symbols layout for arabic language -->
|
||||
|
|
|
@ -90,6 +90,10 @@
|
|||
android:key="custom_symbols_number_layouts"
|
||||
android:title="@string/customize_symbols_number_layouts" />
|
||||
|
||||
<Preference
|
||||
android:key="custom_functional_key_layouts"
|
||||
android:title="@string/customize_functional_key_layouts" />
|
||||
|
||||
<Preference
|
||||
android:key="backup_restore"
|
||||
android:title="@string/backup_restore_title" />
|
||||
|
|
18
layouts.md
18
layouts.md
|
@ -117,13 +117,15 @@ You can also specify special key codes like `a|!code/key_action_previous`, but i
|
|||
* If a newly added language does not use latin script, please update the default scripts method `Locale.script` in [ScriptUtils](app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt)
|
||||
|
||||
## Functional key layouts
|
||||
This is not yet customizable, but will be soon!
|
||||
Mostly customizing functional keys works like other layouts, with some specific adjustments:
|
||||
* you can either have a single layout for functional keys (default), or separate layouts for symbols and shift symbols
|
||||
* when using a single layout
|
||||
* emoji and language switch keys will only show in alphabet layout and when the option is enabled
|
||||
* numpad key will only show in symbols layout
|
||||
* otherwise the layout will be shown as it is in the layout file
|
||||
Customizing functional keys mostly works like other layouts, with some specific adjustments:
|
||||
* When using the default functional layout, emoji, language switch and numpad keys are actually always in the layout, but get removed depending on settings and the main layout (alphabet, symbols or more symbols). This removal is disabled when you customize any functional layout, so to not block you from adding e.g. a numpad key in alphabet layout.
|
||||
* When you use a language that has a ZWNJ key, the key will automatically be added to the right of the (first) space bar in the bottom row
|
||||
* Adding popups to keys that switch layout does not work properly, as usually the layout is switched as soon as the key gets pressed.
|
||||
* use keys with `"type": "placeholder"` for
|
||||
* separating left and right functional keys (e.g. shift and delete in default layout)
|
||||
* separating top and bottom rows in case you want to have functional key rows aligned to the top of the keyboard
|
||||
* separating top and bottom rows in case you want to have functional key rows aligned to the top of the keyboard (add a row with the placeholder as the only key)
|
||||
* if the last row in functional keys does not contain a placeholder, it is used as bottom row (like in the default functional layout)
|
||||
* When you functional keys only for some of alphabet, symbols and more symbols, behavior is as follows
|
||||
* more symbols will fall back to symbols, then normal
|
||||
* symbols will fall back to normal, then default (if you only customized more symbols functional layout)
|
||||
* normal will fall back to default (if you only customized symbols and/or more symbols functional layout)
|
||||
|
|
Loading…
Add table
Reference in a new issue