mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-20 22:29:10 +00:00
Merge branch 'Helium314:main' into inline-code-point-loops
This commit is contained in:
commit
84f73cc3da
13 changed files with 246 additions and 100 deletions
|
@ -59,8 +59,7 @@ object LayoutParser {
|
|||
|
||||
/** Parse simple layouts, defined only as rows of (normal) keys with popup keys. */
|
||||
fun parseSimpleString(layoutText: String): List<List<KeyData>> {
|
||||
val rowStrings = layoutText.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex()).filter { it.isNotBlank() }
|
||||
return rowStrings.map { row ->
|
||||
return LayoutUtils.getSimpleRowStrings(layoutText).map { row ->
|
||||
row.split("\n").mapNotNull { parseKey(it) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ fun checkVersionUpgrade(context: Context) {
|
|||
split[1] = newName
|
||||
split.joinToString(":")
|
||||
}
|
||||
Settings.writePrefAdditionalSubtypes(prefs, newSubtypeStrings.joinToString(";"))
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, newSubtypeStrings.joinToString(";")).apply()
|
||||
}
|
||||
// rename other custom layouts
|
||||
LayoutUtilsCustom.onLayoutFileChanged()
|
||||
|
@ -630,7 +630,7 @@ private fun upgradesWhenComingFromOldAppName(context: Context) {
|
|||
val localeString = it.substringBefore(":")
|
||||
additionalSubtypes.add(it.replace(localeString, localeString.constructLocale().toLanguageTag()))
|
||||
}
|
||||
Settings.writePrefAdditionalSubtypes(prefs, additionalSubtypes.joinToString(";"))
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, additionalSubtypes.joinToString(";")).apply()
|
||||
}
|
||||
// move pinned clips to credential protected storage if device is not locked (should never happen)
|
||||
if (!prefs.contains(Settings.PREF_PINNED_CLIPS)) return
|
||||
|
|
|
@ -523,6 +523,11 @@ public class LatinIME extends InputMethodService implements
|
|||
}
|
||||
|
||||
final class SubtypeState {
|
||||
// When HintLocales causes a subtype override, we store
|
||||
// the overridden subtype here in order to restore it when
|
||||
// we switch to another input context that has no HintLocales.
|
||||
private InputMethodSubtype mOverriddenByLocale;
|
||||
|
||||
private InputMethodSubtype mLastActiveSubtype;
|
||||
private boolean mCurrentSubtypeHasBeenUsed = true; // starting with true avoids immediate switch
|
||||
|
||||
|
@ -530,6 +535,70 @@ public class LatinIME extends InputMethodService implements
|
|||
mCurrentSubtypeHasBeenUsed = true;
|
||||
}
|
||||
|
||||
// TextFields can provide locale/language hints that the IME should use via 'hintLocales'.
|
||||
// If a matching subtype is found, we temporarily switch to that subtype until
|
||||
// we return to a context that does not provide any hints, or until the user
|
||||
// explicitly changes the language/subtype in use.
|
||||
public InputMethodSubtype getSubtypeForLocales(final RichInputMethodManager richImm, final Iterable<Locale> locales) {
|
||||
final InputMethodSubtype overriddenByLocale = mOverriddenByLocale;
|
||||
if (locales == null) {
|
||||
if (overriddenByLocale != null) {
|
||||
// no locales provided, so switch back to
|
||||
// whatever subtype was used last time.
|
||||
mOverriddenByLocale = null;
|
||||
|
||||
return overriddenByLocale;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
final InputMethodSubtype currentSubtype = richImm.getCurrentSubtype().getRawSubtype();
|
||||
final Locale currentSubtypeLocale = richImm.getCurrentSubtypeLocale();
|
||||
final int minimumMatchLevel = 3; // LocaleUtils.LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
|
||||
|
||||
// Try finding a subtype matching the hint language.
|
||||
for (final Locale hintLocale : locales) {
|
||||
if (LocaleUtils.INSTANCE.getMatchLevel(hintLocale, currentSubtypeLocale) >= minimumMatchLevel
|
||||
|| CollectionsKt.any(mSettings.getCurrent().mSecondaryLocales,
|
||||
(secLocale) -> LocaleUtils.INSTANCE.getMatchLevel(hintLocale, secLocale) >= minimumMatchLevel)) {
|
||||
// current locales are already a good match, and we want to avoid unnecessary layout switches.
|
||||
return null;
|
||||
}
|
||||
|
||||
final InputMethodSubtype subtypeForHintLocale = richImm.findSubtypeForHintLocale(hintLocale);
|
||||
if (subtypeForHintLocale == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subtypeForHintLocale.equals(currentSubtype)) {
|
||||
// no need to switch, we already use the correct locale.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (overriddenByLocale == null) {
|
||||
// auto-switching based on hint locale, so store
|
||||
// whatever subtype was in use so we can switch back
|
||||
// to it later when there are no hint locales.
|
||||
mOverriddenByLocale = currentSubtype;
|
||||
}
|
||||
|
||||
return subtypeForHintLocale;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onSubtypeChanged(final InputMethodSubtype oldSubtype,
|
||||
final InputMethodSubtype newSubtype) {
|
||||
if (oldSubtype != mOverriddenByLocale) {
|
||||
// Whenever the subtype is changed, clear tracking
|
||||
// the subtype that is overridden by a HintLocale as
|
||||
// we no longer have a subtype to automatically switch back to.
|
||||
mOverriddenByLocale = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void switchSubtype(final RichInputMethodManager richImm) {
|
||||
final InputMethodSubtype currentSubtype = richImm.getCurrentSubtype().getRawSubtype();
|
||||
final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype;
|
||||
|
@ -858,6 +927,8 @@ public class LatinIME extends InputMethodService implements
|
|||
return;
|
||||
}
|
||||
InputMethodSubtype oldSubtype = mRichImm.getCurrentSubtype().getRawSubtype();
|
||||
|
||||
mSubtypeState.onSubtypeChanged(oldSubtype, subtype);
|
||||
StatsUtils.onSubtypeChanged(oldSubtype, subtype);
|
||||
mRichImm.onSubtypeChanged(subtype);
|
||||
mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype),
|
||||
|
@ -876,20 +947,10 @@ public class LatinIME extends InputMethodService implements
|
|||
super.onStartInput(editorInfo, restarting);
|
||||
|
||||
final List<Locale> hintLocales = EditorInfoCompatUtils.getHintLocales(editorInfo);
|
||||
if (hintLocales == null) {
|
||||
return;
|
||||
}
|
||||
// Try switching to a subtype matching the hint language.
|
||||
for (final Locale hintLocale : hintLocales) {
|
||||
if (LocaleUtils.INSTANCE.getMatchLevel(hintLocale, mRichImm.getCurrentSubtypeLocale()) >= 3
|
||||
|| CollectionsKt.any(mSettings.getCurrent().mSecondaryLocales, (secLocale) -> LocaleUtils.INSTANCE.getMatchLevel(hintLocale, secLocale) >= 3))
|
||||
return; // current locales are already a good match, and we want to avoid unnecessary layout switches
|
||||
final InputMethodSubtype newSubtype = mRichImm.findSubtypeForHintLocale(hintLocale);
|
||||
if (newSubtype == null) continue;
|
||||
if (newSubtype.equals(mRichImm.getCurrentSubtype().getRawSubtype()))
|
||||
return; // no need to switch, we already use the correct locale
|
||||
mHandler.postSwitchLanguage(newSubtype);
|
||||
break;
|
||||
final InputMethodSubtype subtypeForLocales = mSubtypeState.getSubtypeForLocales(mRichImm, hintLocales);
|
||||
if (subtypeForLocales != null) {
|
||||
// found a better subtype using hint locales that we should switch to.
|
||||
mHandler.postSwitchLanguage(subtypeForLocales);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import helium314.keyboard.keyboard.KeyboardTheme
|
|||
import helium314.keyboard.latin.BuildConfig
|
||||
import helium314.keyboard.latin.common.Constants.Separators
|
||||
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue
|
||||
import helium314.keyboard.latin.utils.JniUtils
|
||||
import helium314.keyboard.latin.utils.LayoutType
|
||||
import helium314.keyboard.latin.utils.POPUP_KEYS_LABEL_DEFAULT
|
||||
import helium314.keyboard.latin.utils.POPUP_KEYS_ORDER_DEFAULT
|
||||
|
@ -74,9 +73,9 @@ object Defaults {
|
|||
const val PREF_LANGUAGE_SWITCH_KEY = "internal"
|
||||
const val PREF_SHOW_EMOJI_KEY = false
|
||||
const val PREF_VARIABLE_TOOLBAR_DIRECTION = true
|
||||
const val PREF_ADDITIONAL_SUBTYPES = "de${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=qwerty${Separators.SETS}" +
|
||||
"fr${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=qwertz${Separators.SETS}" +
|
||||
"hu${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=qwerty"
|
||||
const val PREF_ADDITIONAL_SUBTYPES = "de${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN:qwerty${Separators.SETS}" +
|
||||
"fr${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN:qwertz${Separators.SETS}" +
|
||||
"hu${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN:qwerty"
|
||||
const val PREF_ENABLE_SPLIT_KEYBOARD = false
|
||||
const val PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = false
|
||||
const val PREF_SPLIT_SPACER_SCALE = SettingsValues.DEFAULT_SIZE_SCALE
|
||||
|
@ -152,8 +151,6 @@ object Defaults {
|
|||
const val PREF_EMOJI_RECENT_KEYS = ""
|
||||
const val PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID = 0
|
||||
const val PREF_PINNED_CLIPS = ""
|
||||
@JvmField
|
||||
val PREF_LIBRARY_CHECKSUM: String = JniUtils.expectedDefaultChecksum()
|
||||
const val PREF_SHOW_DEBUG_SETTINGS = false
|
||||
val PREF_DEBUG_MODE = BuildConfig.DEBUG
|
||||
const val PREF_SHOW_SUGGESTION_INFOS = false
|
||||
|
|
|
@ -312,10 +312,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
mPrefs.edit().putBoolean(Settings.PREF_ALWAYS_INCOGNITO_MODE, !oldValue).apply();
|
||||
}
|
||||
|
||||
public static void writePrefAdditionalSubtypes(final SharedPreferences prefs, final String prefSubtypes) {
|
||||
prefs.edit().putString(PREF_ADDITIONAL_SUBTYPES, prefSubtypes).apply();
|
||||
}
|
||||
|
||||
public static int readHorizontalSpaceSwipe(final SharedPreferences prefs) {
|
||||
return switch (prefs.getString(PREF_SPACE_HORIZONTAL_SWIPE, Defaults.PREF_SPACE_HORIZONTAL_SWIPE)) {
|
||||
case "move_cursor" -> KeyboardActionListener.SWIPE_MOVE_CURSOR;
|
||||
|
|
|
@ -11,11 +11,9 @@ import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
|||
import helium314.keyboard.latin.define.DebugFlags
|
||||
import helium314.keyboard.latin.utils.LayoutType
|
||||
import helium314.keyboard.latin.utils.LayoutType.Companion.toExtraValue
|
||||
import helium314.keyboard.latin.utils.LayoutUtilsCustom
|
||||
import helium314.keyboard.latin.utils.Log
|
||||
import helium314.keyboard.latin.utils.ScriptUtils
|
||||
import helium314.keyboard.latin.utils.ScriptUtils.script
|
||||
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
|
||||
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
|
||||
import helium314.keyboard.latin.utils.locale
|
||||
import java.util.Locale
|
||||
|
@ -27,23 +25,9 @@ data class SettingsSubtype(val locale: Locale, val extraValues: String) {
|
|||
|
||||
/** Creates an additional subtype from the SettingsSubtype.
|
||||
* Resulting InputMethodSubtypes are equal if SettingsSubtypes are equal */
|
||||
fun toAdditionalSubtype(): InputMethodSubtype? {
|
||||
fun toAdditionalSubtype(): InputMethodSubtype {
|
||||
val asciiCapable = locale.script() == ScriptUtils.SCRIPT_LATIN
|
||||
val subtype = SubtypeUtilsAdditional.createAdditionalSubtype(locale, extraValues, asciiCapable, true)
|
||||
|
||||
// todo: this is returns null for all non-latin layouts
|
||||
// either fix it, or remove the check
|
||||
// if removed, removing a layout will result in fallback qwerty even for non-ascii, but this is better than the current alternative
|
||||
/* if (subtype.nameResId == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT
|
||||
&& mainLayoutName()?.endsWith("+") != true // "+" layouts and custom layouts are always "unknown"
|
||||
&& !LayoutUtilsCustom.isCustomLayout(mainLayoutName() ?: SubtypeLocaleUtils.QWERTY)
|
||||
) {
|
||||
// Skip unknown keyboard layout subtype. This may happen when predefined keyboard
|
||||
// layout has been removed.
|
||||
Log.w(SettingsSubtype::class.simpleName, "unknown additional subtype $this")
|
||||
return null
|
||||
}*/
|
||||
return subtype
|
||||
return SubtypeUtilsAdditional.createAdditionalSubtype(locale, extraValues, asciiCapable, true)
|
||||
}
|
||||
|
||||
fun mainLayoutName() = LayoutType.getMainLayoutFromExtraValue(extraValues)
|
||||
|
@ -54,7 +38,7 @@ data class SettingsSubtype(val locale: Locale, val extraValues: String) {
|
|||
val newList = extraValues.split(",")
|
||||
.filterNot { it.isBlank() || it.startsWith("$extraValueKey=") || it == extraValueKey }
|
||||
val newValue = if (extraValue == null) extraValueKey else "$extraValueKey=$extraValue"
|
||||
val newValues = (newList + newValue).joinToString(",")
|
||||
val newValues = (newList + newValue).sorted().joinToString(",")
|
||||
return copy(extraValues = newValues)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.text.TextUtils;
|
|||
|
||||
import helium314.keyboard.latin.App;
|
||||
import helium314.keyboard.latin.BuildConfig;
|
||||
import helium314.keyboard.latin.settings.Defaults;
|
||||
import helium314.keyboard.latin.settings.Settings;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -63,7 +62,7 @@ public final class JniUtils {
|
|||
// we want the default preferences, because storing the checksum in device protected storage is discouraged
|
||||
// see https://developer.android.com/reference/android/content/Context#createDeviceProtectedStorageContext()
|
||||
// if device is locked, this will throw an IllegalStateException
|
||||
wantedChecksum = KtxKt.protectedPrefs(app).getString(Settings.PREF_LIBRARY_CHECKSUM, Defaults.PREF_LIBRARY_CHECKSUM);
|
||||
wantedChecksum = KtxKt.protectedPrefs(app).getString(Settings.PREF_LIBRARY_CHECKSUM, expectedDefaultChecksum());
|
||||
}
|
||||
final FileInputStream libStream = new FileInputStream(userSuppliedLibrary);
|
||||
final String checksum = ChecksumCalculator.INSTANCE.checksum(libStream);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package helium314.keyboard.latin.utils
|
||||
|
||||
import android.content.Context
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.SimplePopups
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.getOrCreate
|
||||
import helium314.keyboard.latin.R
|
||||
import helium314.keyboard.latin.settings.Defaults.default
|
||||
import helium314.keyboard.latin.utils.LayoutType.Companion.folder
|
||||
|
@ -26,6 +28,7 @@ object LayoutUtils {
|
|||
fun getLMainLayoutsForLocales(locales: List<Locale>, context: Context): Collection<String> =
|
||||
locales.flatMapTo(HashSet()) { getAvailableLayouts(LayoutType.MAIN, context, it) }.sorted()
|
||||
|
||||
/** gets content for built-in (non-custom) layout [layoutName], with fallback to qwerty */
|
||||
fun getContent(layoutType: LayoutType, layoutName: String, context: Context): String {
|
||||
val layouts = context.assets.list(layoutType.folder)!!
|
||||
layouts.firstOrNull { it.startsWith("$layoutName.") }
|
||||
|
@ -33,4 +36,27 @@ object LayoutUtils {
|
|||
val fallback = layouts.first { it.startsWith(layoutType.default) } // must exist!
|
||||
return context.assets.open(layoutType.folder + File.separator + fallback).reader().readText()
|
||||
}
|
||||
|
||||
fun getContentWithPlus(mainLayoutName: String, locale: Locale, context: Context): String {
|
||||
val content = getContent(LayoutType.MAIN, mainLayoutName, context)
|
||||
if (!mainLayoutName.endsWith("+"))
|
||||
return content
|
||||
// the stuff below will not work if we add "+" layouts in json format
|
||||
// ideally we should serialize keyData to json to solve this
|
||||
val rows = getSimpleRowStrings(content)
|
||||
val localeKeyboardInfos = getOrCreate(context, locale)
|
||||
return rows.mapIndexed { i, row ->
|
||||
val extraKeys = localeKeyboardInfos.getExtraKeys(i + 1) ?: return@mapIndexed row
|
||||
val rowList = row.split("\n").filterNot { it.isEmpty() }.toMutableList()
|
||||
extraKeys.forEach { key ->
|
||||
val popups = (key.popup as? SimplePopups)?.popupKeys?.joinToString(" ")
|
||||
?.takeIf { it.isNotEmpty() }?.let { " $it" } ?: ""
|
||||
rowList.add(key.label + popups)
|
||||
}
|
||||
rowList.joinToString("\n")
|
||||
}.joinToString("\n\n")
|
||||
}
|
||||
|
||||
fun getSimpleRowStrings(layoutContent: String): List<String> =
|
||||
layoutContent.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex()).filter { it.isNotBlank() }
|
||||
}
|
||||
|
|
|
@ -52,9 +52,8 @@ object SubtypeSettings {
|
|||
|
||||
fun addEnabledSubtype(prefs: SharedPreferences, newSubtype: InputMethodSubtype) {
|
||||
val subtype = newSubtype.toSettingsSubtype()
|
||||
val subtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!
|
||||
.split(Separators.SETS).filter { it.isNotBlank() }.map { it.toSettingsSubtype() } + subtype
|
||||
val newString = subtypes.map { it.toPref() }.toSortedSet().joinToString(Separators.SETS)
|
||||
val subtypes = createSettingsSubtypes(prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!) + subtype
|
||||
val newString = createPrefSubtypes(subtypes)
|
||||
prefs.edit { putString(Settings.PREF_ENABLED_SUBTYPES, newString) }
|
||||
|
||||
if (newSubtype !in enabledSubtypes) {
|
||||
|
@ -74,10 +73,8 @@ object SubtypeSettings {
|
|||
|
||||
fun getSelectedSubtype(prefs: SharedPreferences): InputMethodSubtype {
|
||||
val selectedSubtype = prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)!!.toSettingsSubtype()
|
||||
if (selectedSubtype.isAdditionalSubtype(prefs)) {
|
||||
val selectedAdditionalSubtype = selectedSubtype.toAdditionalSubtype()
|
||||
if (selectedAdditionalSubtype != null) return selectedAdditionalSubtype
|
||||
}
|
||||
if (selectedSubtype.isAdditionalSubtype(prefs))
|
||||
return selectedSubtype.toAdditionalSubtype()
|
||||
// no additional subtype, must be a resource subtype
|
||||
|
||||
val subtype = enabledSubtypes.firstOrNull { it.toSettingsSubtype() == selectedSubtype }
|
||||
|
@ -157,6 +154,15 @@ object SubtypeSettings {
|
|||
RichInputMethodManager.getInstance().refreshSubtypeCaches()
|
||||
}
|
||||
|
||||
fun createSettingsSubtypes(prefSubtypes: String): List<SettingsSubtype> =
|
||||
prefSubtypes.split(Separators.SETS).mapNotNull {
|
||||
if (it.isEmpty()) null
|
||||
else it.toSettingsSubtype()
|
||||
}
|
||||
|
||||
fun createPrefSubtypes(subtypes: Collection<SettingsSubtype>): String =
|
||||
subtypes.map { it.toPref() }.toSortedSet().joinToString(Separators.SETS)
|
||||
|
||||
fun init(context: Context) {
|
||||
SubtypeLocaleUtils.init(context) // necessary to get the correct getKeyboardLayoutSetName
|
||||
|
||||
|
@ -207,7 +213,8 @@ object SubtypeSettings {
|
|||
}
|
||||
if (subtypesToRemove.isEmpty()) return
|
||||
Log.w(TAG, "removing custom subtypes without main layout files: $subtypesToRemove")
|
||||
Settings.writePrefAdditionalSubtypes(prefs, additionalSubtypes.filterNot { it in subtypesToRemove }.joinToString(Separators.SETS))
|
||||
// todo: now we have a qwerty fallback anyway, consider removing this method (makes bugs more obvious to users)
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, additionalSubtypes.filterNot { it in subtypesToRemove }.joinToString(Separators.SETS)).apply()
|
||||
}
|
||||
|
||||
private fun loadAdditionalSubtypes(prefs: SharedPreferences) {
|
||||
|
@ -220,15 +227,11 @@ object SubtypeSettings {
|
|||
// requires loadResourceSubtypes to be called before
|
||||
private fun loadEnabledSubtypes(context: Context) {
|
||||
val prefs = context.prefs()
|
||||
val settingsSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!
|
||||
.split(Separators.SETS).filter { it.isNotEmpty() }.map { it.toSettingsSubtype() }
|
||||
val settingsSubtypes = createSettingsSubtypes(prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!)
|
||||
for (settingsSubtype in settingsSubtypes) {
|
||||
if (settingsSubtype.isAdditionalSubtype(prefs)) {
|
||||
val additionalSubtype = settingsSubtype.toAdditionalSubtype()
|
||||
if (additionalSubtype != null) {
|
||||
enabledSubtypes.add(additionalSubtype)
|
||||
continue
|
||||
}
|
||||
enabledSubtypes.add(settingsSubtype.toAdditionalSubtype())
|
||||
continue
|
||||
}
|
||||
val subtypesForLocale = resourceSubtypesByLocale[settingsSubtype.locale]
|
||||
if (subtypesForLocale == null) {
|
||||
|
@ -258,12 +261,11 @@ object SubtypeSettings {
|
|||
|
||||
/** @return whether pref was changed */
|
||||
private fun removeEnabledSubtype(prefs: SharedPreferences, subtype: SettingsSubtype): Boolean {
|
||||
val oldSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!
|
||||
.split(Separators.SETS).filter { it.isNotEmpty() }.map { it.toSettingsSubtype() }
|
||||
val oldSubtypes = createSettingsSubtypes(prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!)
|
||||
val newSubtypes = oldSubtypes - subtype
|
||||
if (oldSubtypes == newSubtypes)
|
||||
return false // already removed
|
||||
prefs.edit { putString(Settings.PREF_ENABLED_SUBTYPES, newSubtypes.joinToString(Separators.SETS) { it.toPref() }) }
|
||||
prefs.edit { putString(Settings.PREF_ENABLED_SUBTYPES, createPrefSubtypes(newSubtypes)) }
|
||||
if (subtype == prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)!!.toSettingsSubtype()) {
|
||||
// switch subtype if the currently used one has been disabled
|
||||
try {
|
||||
|
|
|
@ -53,10 +53,11 @@ object SubtypeUtilsAdditional {
|
|||
val prefs = context.prefs()
|
||||
SubtypeSettings.removeEnabledSubtype(context, subtype)
|
||||
val oldAdditionalSubtypesString = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
|
||||
val oldAdditionalSubtypes = createAdditionalSubtypes(oldAdditionalSubtypesString)
|
||||
val newAdditionalSubtypes = oldAdditionalSubtypes.filter { it != subtype }
|
||||
val newAdditionalSubtypesString = createPrefSubtypes(newAdditionalSubtypes)
|
||||
Settings.writePrefAdditionalSubtypes(prefs, newAdditionalSubtypesString)
|
||||
val oldAdditionalSubtypes = SubtypeSettings.createSettingsSubtypes(oldAdditionalSubtypesString)
|
||||
val settingsSubtype = subtype.toSettingsSubtype()
|
||||
val newAdditionalSubtypes = oldAdditionalSubtypes.filter { it != settingsSubtype }
|
||||
val newAdditionalSubtypesString = SubtypeSettings.createPrefSubtypes(newAdditionalSubtypes)
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, newAdditionalSubtypesString).apply()
|
||||
}
|
||||
|
||||
// updates additional subtypes, enabled subtypes, and selected subtype
|
||||
|
@ -66,34 +67,37 @@ object SubtypeUtilsAdditional {
|
|||
val isSelected = prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)!!.toSettingsSubtype() == from
|
||||
val isEnabled = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!.split(Separators.SETS)
|
||||
.any { it.toSettingsSubtype() == from }
|
||||
val new = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
|
||||
.split(Separators.SETS).mapNotNullTo(sortedSetOf()) {
|
||||
if (it == from.toPref()) null else it
|
||||
} + to.toPref()
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, new.joinToString(Separators.SETS)).apply()
|
||||
|
||||
val fromSubtype = from.toAdditionalSubtype() // will be null if we edit a resource subtype
|
||||
val toSubtype = to.toAdditionalSubtype() // should never be null
|
||||
if (isSelected && toSubtype != null) {
|
||||
SubtypeSettings.setSelectedSubtype(prefs, toSubtype)
|
||||
val additionalSubtypes = SubtypeSettings.createSettingsSubtypes(prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!)
|
||||
.toMutableList()
|
||||
additionalSubtypes.remove(from)
|
||||
if (SubtypeSettings.getResourceSubtypesForLocale(to.locale).none { it.toSettingsSubtype() == to }) {
|
||||
// We only add the "to" subtype if it's not equal to a resource subtype.
|
||||
// This means we make additional subtype disappear as magically as it was added if all settings are default.
|
||||
// If we don't do this, enabling the base subtype will result in the additional subtype being enabled,
|
||||
// as both have the same settingsSubtype.
|
||||
additionalSubtypes.add(to)
|
||||
}
|
||||
if (fromSubtype != null && isEnabled && toSubtype != null) {
|
||||
SubtypeSettings.removeEnabledSubtype(context, fromSubtype)
|
||||
SubtypeSettings.addEnabledSubtype(prefs, toSubtype)
|
||||
val editor = prefs.edit()
|
||||
editor.putString(Settings.PREF_ADDITIONAL_SUBTYPES, SubtypeSettings.createPrefSubtypes(additionalSubtypes))
|
||||
if (isSelected) {
|
||||
editor.putString(Settings.PREF_SELECTED_SUBTYPE, to.toPref())
|
||||
}
|
||||
if (isEnabled) {
|
||||
val enabled = SubtypeSettings.createSettingsSubtypes(prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!)
|
||||
.toMutableList()
|
||||
enabled.remove(from)
|
||||
enabled.add(to)
|
||||
editor.putString(Settings.PREF_ENABLED_SUBTYPES, SubtypeSettings.createPrefSubtypes(enabled))
|
||||
}
|
||||
editor.apply()
|
||||
SubtypeSettings.reloadEnabledSubtypes(context)
|
||||
}
|
||||
|
||||
fun createAdditionalSubtypes(prefSubtypes: String): List<InputMethodSubtype> {
|
||||
if (prefSubtypes.isEmpty())
|
||||
return emptyList()
|
||||
return prefSubtypes.split(Separators.SETS).mapNotNull { it.toSettingsSubtype().toAdditionalSubtype() }
|
||||
}
|
||||
|
||||
fun createPrefSubtypes(subtypes: Collection<InputMethodSubtype>): String {
|
||||
if (subtypes.isEmpty())
|
||||
return ""
|
||||
return subtypes.joinToString(Separators.SETS) { it.toSettingsSubtype().toPref() }
|
||||
}
|
||||
fun createAdditionalSubtypes(prefSubtypes: String): List<InputMethodSubtype> =
|
||||
prefSubtypes.split(Separators.SETS).mapNotNull {
|
||||
if (it.isEmpty()) null
|
||||
else it.toSettingsSubtype().toAdditionalSubtype()
|
||||
}
|
||||
|
||||
private fun getNameResId(locale: Locale, mainLayoutName: String): Int {
|
||||
val nameId = SubtypeLocaleUtils.getSubtypeNameResId(locale, mainLayoutName)
|
||||
|
|
|
@ -125,8 +125,8 @@ fun SubtypeDialog(
|
|||
onConfirmed = { onConfirmed(currentSubtype) },
|
||||
neutralButtonText = if (initialSubtype.isAdditionalSubtype(prefs)) stringResource(R.string.delete) else null,
|
||||
onNeutral = {
|
||||
SubtypeUtilsAdditional.removeAdditionalSubtype(ctx, initialSubtype.toAdditionalSubtype()!!)
|
||||
SubtypeSettings.removeEnabledSubtype(ctx, initialSubtype.toAdditionalSubtype()!!)
|
||||
SubtypeUtilsAdditional.removeAdditionalSubtype(ctx, initialSubtype.toAdditionalSubtype())
|
||||
SubtypeSettings.removeEnabledSubtype(ctx, initialSubtype.toAdditionalSubtype())
|
||||
onDismissRequest()
|
||||
},
|
||||
title = {
|
||||
|
@ -393,7 +393,7 @@ private fun MainLayoutRow(
|
|||
if (showLayoutEditDialog != null) {
|
||||
val layoutName = showLayoutEditDialog!!.first
|
||||
val startContent = showLayoutEditDialog?.second
|
||||
?: if (layoutName in appLayouts) LayoutUtils.getContent(LayoutType.MAIN, layoutName, ctx)
|
||||
?: if (layoutName in appLayouts) LayoutUtils.getContentWithPlus(layoutName, currentSubtype.locale, ctx)
|
||||
else null
|
||||
LayoutEditDialog(
|
||||
onDismissRequest = { showLayoutEditDialog = null },
|
||||
|
|
|
@ -35,10 +35,13 @@ fun LoadGestureLibPreference(setting: Setting) {
|
|||
val abi = Build.SUPPORTED_ABIS[0]
|
||||
val libFile = File(ctx.filesDir?.absolutePath + File.separator + JniUtils.JNI_LIB_IMPORT_FILE_NAME)
|
||||
fun renameToLibFileAndRestart(file: File, checksum: String) {
|
||||
libFile.setWritable(true)
|
||||
libFile.delete()
|
||||
// store checksum in default preferences (soo JniUtils)
|
||||
// store checksum in default preferences (see JniUtils)
|
||||
prefs.edit().putString(Settings.PREF_LIBRARY_CHECKSUM, checksum).commit()
|
||||
file.renameTo(libFile)
|
||||
file.copyTo(libFile)
|
||||
libFile.setReadOnly()
|
||||
file.delete()
|
||||
Runtime.getRuntime().exit(0) // exit will restart the app, so library will be loaded
|
||||
}
|
||||
var tempFilePath: String? by rememberSaveable { mutableStateOf(null) }
|
||||
|
|
75
app/src/test/java/helium314/keyboard/SubtypeTest.kt
Normal file
75
app/src/test/java/helium314/keyboard/SubtypeTest.kt
Normal file
|
@ -0,0 +1,75 @@
|
|||
package helium314.keyboard
|
||||
|
||||
import helium314.keyboard.keyboard.KeyboardId
|
||||
import helium314.keyboard.keyboard.KeyboardLayoutSet
|
||||
import helium314.keyboard.keyboard.internal.KeyboardParams
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
|
||||
import helium314.keyboard.latin.LatinIME
|
||||
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||
import helium314.keyboard.latin.settings.Settings
|
||||
import helium314.keyboard.latin.settings.SettingsSubtype.Companion.toSettingsSubtype
|
||||
import helium314.keyboard.latin.utils.LayoutType
|
||||
import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT
|
||||
import helium314.keyboard.latin.utils.SubtypeSettings
|
||||
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
|
||||
import helium314.keyboard.latin.utils.prefs
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.shadows.ShadowLog
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(shadows = [
|
||||
ShadowInputMethodManager2::class
|
||||
])
|
||||
class SubtypeTest {
|
||||
private lateinit var latinIME: LatinIME
|
||||
private lateinit var params: KeyboardParams
|
||||
|
||||
@BeforeTest fun setUp() {
|
||||
latinIME = Robolectric.setupService(LatinIME::class.java)
|
||||
ShadowLog.setupLogging()
|
||||
ShadowLog.stream = System.out
|
||||
params = KeyboardParams()
|
||||
params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET)
|
||||
params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT)
|
||||
addLocaleKeyTextsToParams(latinIME, params, POPUP_KEYS_NORMAL)
|
||||
}
|
||||
|
||||
@Test fun emptyAdditionalSubtypesResultsInEmptyList() {
|
||||
// avoid issues where empty string results in additional subtype for undefined locale
|
||||
val prefs = latinIME.prefs()
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, "").apply()
|
||||
assertTrue(SubtypeSettings.getAdditionalSubtypes().isEmpty())
|
||||
val from = SubtypeSettings.getResourceSubtypesForLocale("es".constructLocale()).first()
|
||||
|
||||
// no change, and "changed" subtype actually is resource subtype -> still expect empty list
|
||||
SubtypeUtilsAdditional.changeAdditionalSubtype(from.toSettingsSubtype(), from.toSettingsSubtype(), latinIME)
|
||||
assertEquals(emptyList(), SubtypeSettings.getAdditionalSubtypes().map { it.toSettingsSubtype() })
|
||||
}
|
||||
|
||||
@Test fun subtypeStaysEnabledOnEdits() {
|
||||
val prefs = latinIME.prefs()
|
||||
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, "").apply() // clear it for convenience
|
||||
|
||||
// edit enabled resource subtype
|
||||
val from = SubtypeSettings.getResourceSubtypesForLocale("es".constructLocale()).first()
|
||||
SubtypeSettings.addEnabledSubtype(prefs, from)
|
||||
val to = from.toSettingsSubtype().withLayout(LayoutType.SYMBOLS, "symbols_arabic")
|
||||
SubtypeUtilsAdditional.changeAdditionalSubtype(from.toSettingsSubtype(), to, latinIME)
|
||||
assertEquals(to, SubtypeSettings.getEnabledSubtypes(false).single().toSettingsSubtype())
|
||||
|
||||
// change the new subtype to effectively be the same as original resource subtype
|
||||
val toNew = to.withoutLayout(LayoutType.SYMBOLS)
|
||||
assertEquals(from.toSettingsSubtype(), toNew)
|
||||
SubtypeUtilsAdditional.changeAdditionalSubtype(to, toNew, latinIME)
|
||||
assertEquals(emptyList(), SubtypeSettings.getAdditionalSubtypes().map { it.toSettingsSubtype() })
|
||||
assertEquals(from.toSettingsSubtype(), SubtypeSettings.getEnabledSubtypes(false).single().toSettingsSubtype())
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue