make the settings overrides in subtype work

This commit is contained in:
Helium314 2025-02-19 23:00:58 +01:00
parent eec197c32c
commit 8d094e268a
20 changed files with 257 additions and 155 deletions

View file

@ -13,8 +13,8 @@ android {
applicationId = "helium314.keyboard" applicationId = "helium314.keyboard"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 2308 versionCode = 2309
versionName = "2.3+dev7" versionName = "2.3+dev8"
ndk { ndk {
abiFilters.clear() abiFilters.clear()
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")) abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))

View file

@ -21,10 +21,12 @@ import helium314.keyboard.latin.utils.DictionaryInfoUtils
import helium314.keyboard.latin.utils.LayoutType import helium314.keyboard.latin.utils.LayoutType
import helium314.keyboard.latin.utils.LayoutType.Companion.folder import helium314.keyboard.latin.utils.LayoutType.Companion.folder
import helium314.keyboard.latin.utils.LayoutUtilsCustom import helium314.keyboard.latin.utils.LayoutUtilsCustom
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_LATIN import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_LATIN
import helium314.keyboard.latin.utils.ScriptUtils.script import helium314.keyboard.latin.utils.ScriptUtils.script
import helium314.keyboard.latin.utils.SettingsSubtype import helium314.keyboard.latin.utils.SettingsSubtype
import helium314.keyboard.latin.utils.SettingsSubtype.Companion.toSettingsSubtype import helium314.keyboard.latin.utils.SettingsSubtype.Companion.toSettingsSubtype
import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
import helium314.keyboard.latin.utils.ToolbarKey import helium314.keyboard.latin.utils.ToolbarKey
import helium314.keyboard.latin.utils.defaultPinnedToolbarPref import helium314.keyboard.latin.utils.defaultPinnedToolbarPref
@ -380,6 +382,7 @@ fun checkVersionUpgrade(context: Context) {
if (it.name in value) if (it.name in value)
prefs.edit().putString(key, value.replace(it.name, newFile.name)).apply() prefs.edit().putString(key, value.replace(it.name, newFile.name)).apply()
} }
LayoutUtilsCustom.onLayoutFileChanged()
} }
} }
if (oldVersion <= 2305) { if (oldVersion <= 2305) {
@ -394,7 +397,7 @@ fun checkVersionUpgrade(context: Context) {
val value = prefs.getString(it, "")!!.replace(":", Separators.SET) val value = prefs.getString(it, "")!!.replace(":", Separators.SET)
prefs.edit().putString(it, value).apply() prefs.edit().putString(it, value).apply()
} }
prefs.all.keys.filter { it.startsWith(Settings.PREF_SECONDARY_LOCALES_PREFIX) }.forEach { prefs.all.keys.filter { it.startsWith("secondary_locales_") }.forEach {
val newValue = prefs.getString(it, "")!!.replace(";", Separators.KV) val newValue = prefs.getString(it, "")!!.replace(";", Separators.KV)
prefs.edit().putString(it, newValue).apply() prefs.edit().putString(it, newValue).apply()
} }
@ -454,6 +457,61 @@ fun checkVersionUpgrade(context: Context) {
prefs.edit().putString(it, prefs.getString(it, "")!!.replace("popup_keys_", "")).apply() prefs.edit().putString(it, prefs.getString(it, "")!!.replace("popup_keys_", "")).apply()
} }
} }
if (oldVersion <= 2308) {
SubtypeSettings.init(context) // not sure, but there may be cases where it's not initialized
SubtypeSettings.reloadEnabledSubtypes(context)
prefs.all.keys.toList().forEach { key ->
if (key.startsWith(Settings.PREF_POPUP_KEYS_ORDER+"_")) {
val locale = key.substringAfter(Settings.PREF_POPUP_KEYS_ORDER+"_").constructLocale()
SubtypeSettings.getEnabledSubtypes(prefs).forEach {
if (it.locale() == locale && !SubtypeSettings.isAdditionalSubtype(it)) {
SubtypeUtilsAdditional.changeAdditionalSubtype(it.toSettingsSubtype(), it.toSettingsSubtype(), context)
}
}
val additional = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, "")!!
additional.split(Separators.SETS).forEach inner@{
val subtype = it.toSettingsSubtype()
if (subtype.locale != locale) return@inner
val newSubtype = subtype.with(ExtraValue.POPUP_ORDER, prefs.getString(key, ""))
SubtypeUtilsAdditional.changeAdditionalSubtype(subtype, newSubtype, context)
}
prefs.edit().remove(key).apply()
}
if (key.startsWith(Settings.PREF_POPUP_KEYS_LABELS_ORDER+"_")) {
val locale = key.substringAfter(Settings.PREF_POPUP_KEYS_LABELS_ORDER+"_").constructLocale()
SubtypeSettings.getEnabledSubtypes(prefs).forEach {
if (it.locale() == locale && !SubtypeSettings.isAdditionalSubtype(it)) {
SubtypeUtilsAdditional.changeAdditionalSubtype(it.toSettingsSubtype(), it.toSettingsSubtype(), context)
}
}
val additional = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, "")!!
additional.split(Separators.SETS).forEach inner@{
val subtype = it.toSettingsSubtype()
if (subtype.locale != locale) return@inner
val newSubtype = subtype.with(ExtraValue.HINT_ORDER, prefs.getString(key, ""))
SubtypeUtilsAdditional.changeAdditionalSubtype(subtype, newSubtype, context)
}
prefs.edit().remove(key).apply()
}
if (key.startsWith("secondary_locales_")) {
val locale = key.substringAfter("secondary_locales_").constructLocale()
SubtypeSettings.getEnabledSubtypes(prefs).forEach {
if (it.locale() == locale && !SubtypeSettings.isAdditionalSubtype(it)) {
SubtypeUtilsAdditional.changeAdditionalSubtype(it.toSettingsSubtype(), it.toSettingsSubtype(), context)
}
}
val additional = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, "")!!
val secondaryLocales = prefs.getString(key, "")!!.split(Separators.KV).filter { it.isNotBlank() }.joinToString(Separators.KV)
additional.split(Separators.SETS).forEach inner@{
val subtype = it.toSettingsSubtype()
if (subtype.locale != locale) return@inner
val newSubtype = subtype.with(ExtraValue.SECONDARY_LOCALES, secondaryLocales)
SubtypeUtilsAdditional.changeAdditionalSubtype(subtype, newSubtype, context)
}
prefs.edit().remove(key).apply()
}
}
}
upgradeToolbarPrefs(prefs) upgradeToolbarPrefs(prefs)
LayoutUtilsCustom.onLayoutFileChanged() // just to be sure LayoutUtilsCustom.onLayoutFileChanged() // just to be sure
prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) } prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) }

View file

@ -57,13 +57,6 @@ public interface DictionaryFacilitator {
*/ */
boolean isForLocale(final Locale locale); boolean isForLocale(final Locale locale);
/**
* Returns whether this facilitator is exactly for this account.
*
* @param account the account to test against.
*/
boolean isForAccount(@Nullable final String account);
interface DictionaryInitializationListener { interface DictionaryInitializationListener {
void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
} }
@ -91,9 +84,12 @@ public interface DictionaryFacilitator {
// useful for multilingual typing // useful for multilingual typing
Locale getCurrentLocale(); Locale getCurrentLocale();
boolean usesContacts(); boolean usesSameSettings(
@NonNull final List<Locale> locales,
boolean usesPersonalization(); final boolean contacts,
final boolean personalization,
@Nullable final String account
);
String getAccount(); String getAccount();

View file

@ -16,6 +16,7 @@ import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.utils.KtxKt; import helium314.keyboard.latin.utils.KtxKt;
import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.Log;
import android.util.LruCache; import android.util.LruCache;
import android.view.inputmethod.InputMethodSubtype;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -31,6 +32,8 @@ import helium314.keyboard.latin.personalization.UserHistoryDictionary;
import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion; import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
import helium314.keyboard.latin.utils.ExecutorUtils; import helium314.keyboard.latin.utils.ExecutorUtils;
import helium314.keyboard.latin.utils.SubtypeSettings;
import helium314.keyboard.latin.utils.SubtypeUtilsKt;
import helium314.keyboard.latin.utils.SuggestionResults; import helium314.keyboard.latin.utils.SuggestionResults;
import java.io.File; import java.io.File;
@ -104,15 +107,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
return false; return false;
} }
/**
* Returns whether this facilitator is exactly for this account.
*
* @param account the account to test against.
*/
public boolean isForAccount(@Nullable final String account) {
return TextUtils.equals(mDictionaryGroups.get(0).mAccount, account);
}
/** /**
* A group of dictionaries that work together for a single language. * A group of dictionaries that work together for a single language.
*/ */
@ -277,12 +271,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
return getCurrentlyPreferredDictionaryGroup().mLocale; return getCurrentlyPreferredDictionaryGroup().mLocale;
} }
@Override
public boolean usesContacts() { public boolean usesContacts() {
return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_CONTACTS) != null; return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_CONTACTS) != null;
} }
@Override
public boolean usesPersonalization() { public boolean usesPersonalization() {
return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_USER_HISTORY) != null; return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_USER_HISTORY) != null;
} }
@ -292,6 +284,19 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
return null; return null;
} }
@Override
public boolean usesSameSettings(@NonNull final List<Locale> locales, final boolean contacts,
final boolean personalization, @Nullable final String account) {
final boolean first = usesContacts() == contacts && usesPersonalization() == personalization
&& TextUtils.equals(mDictionaryGroups.get(0).mAccount, account)
&& locales.size() == mDictionaryGroups.size();
if (!first) return false;
for (int i = 0; i < locales.size(); i++) {
if (locales.get(i) != mDictionaryGroups.get(i).mLocale) return false;
}
return true;
}
@Nullable @Nullable
private static ExpandableBinaryDictionary getSubDict(final String dictType, private static ExpandableBinaryDictionary getSubDict(final String dictType,
final Context context, final Locale locale, final File dictFile, final Context context, final Locale locale, final File dictFile,
@ -339,7 +344,20 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
Log.i(TAG, "resetDictionaries, force reloading main dictionary: " + forceReloadMainDictionary); Log.i(TAG, "resetDictionaries, force reloading main dictionary: " + forceReloadMainDictionary);
final List<Locale> allLocales = new ArrayList<>() {{ final List<Locale> allLocales = new ArrayList<>() {{
add(newLocale); add(newLocale);
addAll(Settings.getSecondaryLocales(KtxKt.prefs(context), newLocale));
// adding secondary locales is a bit tricky since they depend on the subtype
// but usually this is called with the selected subtype locale
final InputMethodSubtype selected = SubtypeSettings.INSTANCE.getSelectedSubtype(KtxKt.prefs(context));
if (SubtypeUtilsKt.locale(selected).equals(newLocale)) {
addAll(SubtypeUtilsKt.getSecondaryLocales(selected.getExtraValue()));
} else {
// probably we're called from the spell checker when using a different app as keyboard
final List<InputMethodSubtype> enabled = SubtypeSettings.INSTANCE.getEnabledSubtypes(KtxKt.prefs(context), false);
for (InputMethodSubtype subtype : enabled) {
if (SubtypeUtilsKt.locale(subtype).equals(newLocale))
addAll(SubtypeUtilsKt.getSecondaryLocales(subtype.getExtraValue()));
}
}
}}; }};
// Do not use contacts dictionary if we do not have permissions to read contacts. // Do not use contacts dictionary if we do not have permissions to read contacts.

View file

@ -669,11 +669,15 @@ public class LatinIME extends InputMethodService implements
} else { } else {
subtypeLocale = subtypeSwitcherLocale; subtypeLocale = subtypeSwitcherLocale;
} }
if (mDictionaryFacilitator.isForLocale(subtypeLocale) final ArrayList<Locale> locales = new ArrayList<>();
&& mDictionaryFacilitator.isForAccount(mSettings.getCurrent().mAccount) locales.add(subtypeLocale);
&& mDictionaryFacilitator.usesContacts() == mSettings.getCurrent().mUseContactsDictionary locales.addAll(mSettings.getCurrent().mSecondaryLocales);
&& mDictionaryFacilitator.usesPersonalization() == mSettings.getCurrent().mUsePersonalizedDicts if (mDictionaryFacilitator.usesSameSettings(
) { locales,
mSettings.getCurrent().mUseContactsDictionary,
mSettings.getCurrent().mUsePersonalizedDicts,
mSettings.getCurrent().mAccount
)) {
return; return;
} }
resetDictionaryFacilitator(subtypeLocale); resetDictionaryFacilitator(subtypeLocale);

View file

@ -319,7 +319,6 @@ public class RichInputMethodManager {
// search for first secondary language & script match // search for first secondary language & script match
final int count = subtypes.size(); final int count = subtypes.size();
final SharedPreferences prefs = KtxKt.prefs(mContext);
final String language = locale.getLanguage(); final String language = locale.getLanguage();
final String script = ScriptUtils.script(locale); final String script = ScriptUtils.script(locale);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
@ -328,7 +327,7 @@ public class RichInputMethodManager {
if (!ScriptUtils.script(subtypeLocale).equals(script)) if (!ScriptUtils.script(subtypeLocale).equals(script))
continue; // need compatible script continue; // need compatible script
bestMatch = subtype; bestMatch = subtype;
final List<Locale> secondaryLocales = Settings.getSecondaryLocales(prefs, subtypeLocale); final List<Locale> secondaryLocales = SubtypeUtilsKt.getSecondaryLocales(subtype.getExtraValue());
for (final Locale secondaryLocale : secondaryLocales) { for (final Locale secondaryLocale : secondaryLocales) {
if (secondaryLocale.getLanguage().equals(language)) { if (secondaryLocale.getLanguage().equals(language)) {
return bestMatch; return bestMatch;

View file

@ -408,8 +408,6 @@ class AdvancedSettingsFragment : SubScreenFragment() {
} }
checkVersionUpgrade(requireContext()) checkVersionUpgrade(requireContext())
Settings.getInstance().startListener() Settings.getInstance().startListener()
val additionalSubtypes = sharedPreferences.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
SubtypeSettings.updateAdditionalSubtypes(SubtypeUtilsAdditional.createAdditionalSubtypes(additionalSubtypes))
SubtypeSettings.reloadEnabledSubtypes(requireContext()) SubtypeSettings.reloadEnabledSubtypes(requireContext())
val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION) val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
activity?.sendBroadcast(newDictBroadcast) activity?.sendBroadcast(newDictBroadcast)

View file

@ -27,6 +27,7 @@ import helium314.keyboard.latin.utils.displayName
import helium314.keyboard.latin.utils.locale import helium314.keyboard.latin.utils.locale
import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.latin.utils.showMissingDictionaryDialog import helium314.keyboard.latin.utils.showMissingDictionaryDialog
import java.util.Locale
class LanguageFilterList(searchField: EditText, recyclerView: RecyclerView) { class LanguageFilterList(searchField: EditText, recyclerView: RecyclerView) {
@ -98,14 +99,14 @@ private class LanguageAdapter(list: List<MutableList<SubtypeInfo>> = listOf(), c
sb.append(string) sb.append(string)
} }
} }
val secondaryLocales = Settings.getSecondaryLocales(prefs, infos.first().subtype.locale()) val secondaryLocales = emptyList<Locale>()
if (secondaryLocales.isNotEmpty()) { if (secondaryLocales.isNotEmpty()) {
if (sb.isNotEmpty()) if (sb.isNotEmpty())
sb.append("\n") sb.append("\n")
sb.append(Settings.getSecondaryLocales(prefs, infos.first().subtype.locale()) //sb.append(Settings.getSecondaryLocales(prefs, infos.first().subtype.locale())
.joinToString(", ") { // .joinToString(", ") {
LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context) // LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context)
}) // })
} }
text = sb text = sb
if (text.isBlank()) isGone = true if (text.isBlank()) isGone = true
@ -126,7 +127,7 @@ private class LanguageAdapter(list: List<MutableList<SubtypeInfo>> = listOf(), c
SubtypeSettings.addEnabledSubtype(prefs, infos.first().subtype) SubtypeSettings.addEnabledSubtype(prefs, infos.first().subtype)
infos.first().isEnabled = true infos.first().isEnabled = true
} else { } else {
SubtypeSettings.removeEnabledSubtype(prefs, infos.first().subtype) SubtypeSettings.removeEnabledSubtype(context, infos.first().subtype)
infos.first().isEnabled = false infos.first().isEnabled = false
} }
} }

View file

@ -188,7 +188,7 @@ class LanguageSettingsDialog(
SubtypeSettings.addEnabledSubtype(prefs, subtype.subtype) SubtypeSettings.addEnabledSubtype(prefs, subtype.subtype)
} }
else else
SubtypeSettings.removeEnabledSubtype(prefs, subtype.subtype) SubtypeSettings.removeEnabledSubtype(context, subtype.subtype)
subtype.isEnabled = b subtype.isEnabled = b
reloadSetting() reloadSetting()
} }
@ -205,7 +205,7 @@ class LanguageSettingsDialog(
//if (isCustom) //if (isCustom)
// LayoutUtilsCustom.removeCustomLayoutFile(layoutSetName, context) // LayoutUtilsCustom.removeCustomLayoutFile(layoutSetName, context)
SubtypeUtilsAdditional.removeAdditionalSubtype(prefs, subtype.subtype) SubtypeUtilsAdditional.removeAdditionalSubtype(prefs, subtype.subtype)
SubtypeSettings.removeEnabledSubtype(prefs, subtype.subtype) SubtypeSettings.removeEnabledSubtype(context, subtype.subtype)
reloadSetting() reloadSetting()
} }
if (isCustom) { if (isCustom) {
@ -226,7 +226,7 @@ class LanguageSettingsDialog(
mainLocale, mainLocale,
infos.first().subtype.isAsciiCapable infos.first().subtype.isAsciiCapable
) )
val selectedSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale) val selectedSecondaryLocales = emptyList<Locale>()// Settings.getSecondaryLocales(prefs, mainLocale)
selectedSecondaryLocales.forEach { selectedSecondaryLocales.forEach {
addSecondaryLocaleView(it) addSecondaryLocaleView(it)
} }
@ -234,14 +234,14 @@ class LanguageSettingsDialog(
binding.addSecondaryLanguage.apply { binding.addSecondaryLanguage.apply {
isVisible = true isVisible = true
setOnClickListener { setOnClickListener {
val locales = (availableSecondaryLocales - Settings.getSecondaryLocales(prefs, mainLocale)).sortedBy { it.displayName } val locales = (availableSecondaryLocales).sortedBy { it.displayName }
val localeNames = locales.map { LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context) }.toTypedArray() val localeNames = locales.map { LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context) }.toTypedArray()
Builder(context) Builder(context)
.setTitle(R.string.button_select_language) .setTitle(R.string.button_select_language)
.setItems(localeNames) { di, i -> .setItems(localeNames) { di, i ->
val locale = locales[i] val locale = locales[i]
val currentSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale) //val currentSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale)
Settings.setSecondaryLocales(prefs, mainLocale, currentSecondaryLocales + locale) //Settings.setSecondaryLocales(prefs, mainLocale, currentSecondaryLocales + locale)
addSecondaryLocaleView(locale) addSecondaryLocaleView(locale)
di.dismiss() di.dismiss()
reloadSetting() reloadSetting()
@ -264,8 +264,8 @@ class LanguageSettingsDialog(
rowBinding.deleteButton.apply { rowBinding.deleteButton.apply {
isVisible = true isVisible = true
setOnClickListener { setOnClickListener {
val currentSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale) //val currentSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocale)
Settings.setSecondaryLocales(prefs, mainLocale, currentSecondaryLocales - locale) //Settings.setSecondaryLocales(prefs, mainLocale, currentSecondaryLocales - locale)
binding.secondaryLocales.removeView(rowBinding.root) binding.secondaryLocales.removeView(rowBinding.root)
reloadSetting() reloadSetting()
reloadDictionaries() reloadDictionaries()

View file

@ -27,8 +27,6 @@ import helium314.keyboard.latin.AudioAndHapticFeedbackManager;
import helium314.keyboard.latin.InputAttributes; import helium314.keyboard.latin.InputAttributes;
import helium314.keyboard.latin.R; import helium314.keyboard.latin.R;
import helium314.keyboard.latin.common.Colors; import helium314.keyboard.latin.common.Colors;
import helium314.keyboard.latin.common.Constants;
import helium314.keyboard.latin.common.LocaleUtils;
import helium314.keyboard.latin.utils.DeviceProtectedUtils; import helium314.keyboard.latin.utils.DeviceProtectedUtils;
import helium314.keyboard.latin.utils.KtxKt; import helium314.keyboard.latin.utils.KtxKt;
import helium314.keyboard.latin.utils.LayoutType; import helium314.keyboard.latin.utils.LayoutType;
@ -43,10 +41,8 @@ import helium314.keyboard.latin.utils.ToolbarUtilsKt;
import helium314.keyboard.settings.SettingsActivity; import helium314.keyboard.settings.SettingsActivity;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -147,7 +143,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_ENABLE_CLIPBOARD_HISTORY = "enable_clipboard_history"; public static final String PREF_ENABLE_CLIPBOARD_HISTORY = "enable_clipboard_history";
public static final String PREF_CLIPBOARD_HISTORY_RETENTION_TIME = "clipboard_history_retention_time"; public static final String PREF_CLIPBOARD_HISTORY_RETENTION_TIME = "clipboard_history_retention_time";
public static final String PREF_SECONDARY_LOCALES_PREFIX = "secondary_locales_";
public static final String PREF_ADD_TO_PERSONAL_DICTIONARY = "add_to_personal_dictionary"; public static final String PREF_ADD_TO_PERSONAL_DICTIONARY = "add_to_personal_dictionary";
public static final String PREF_NAVBAR_COLOR = "navbar_color"; public static final String PREF_NAVBAR_COLOR = "navbar_color";
public static final String PREF_NARROW_KEY_GAPS = "narrow_key_gaps"; public static final String PREF_NARROW_KEY_GAPS = "narrow_key_gaps";
@ -241,8 +236,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
mSettingsValuesLock.unlock(); mSettingsValuesLock.unlock();
} }
if (PREF_ADDITIONAL_SUBTYPES.equals(key)) { if (PREF_ADDITIONAL_SUBTYPES.equals(key)) {
final String additionalSubtypes = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES); SubtypeSettings.INSTANCE.reloadEnabledSubtypes(mContext);
SubtypeSettings.INSTANCE.updateAdditionalSubtypes(SubtypeUtilsAdditional.INSTANCE.createAdditionalSubtypes(additionalSubtypes));
} }
} }
@ -466,29 +460,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
Arrays.fill(sCachedBackgroundImages, null); Arrays.fill(sCachedBackgroundImages, null);
} }
public static List<Locale> getSecondaryLocales(final SharedPreferences prefs, final Locale mainLocale) {
final String localesString = prefs.getString(PREF_SECONDARY_LOCALES_PREFIX + mainLocale.toLanguageTag(), Defaults.PREF_SECONDARY_LOCALES);
final ArrayList<Locale> locales = new ArrayList<>();
for (String languageTag : localesString.split(Constants.Separators.KV)) {
if (languageTag.isEmpty()) continue;
locales.add(LocaleUtils.constructLocale(languageTag));
}
return locales;
}
public static void setSecondaryLocales(final SharedPreferences prefs, final Locale mainLocale, final List<Locale> locales) {
if (locales.isEmpty()) {
prefs.edit().putString(PREF_SECONDARY_LOCALES_PREFIX + mainLocale.toLanguageTag(), "").apply();
return;
}
final StringBuilder sb = new StringBuilder();
for (Locale locale : locales) {
sb.append(Constants.Separators.KV).append(locale.toLanguageTag());
}
prefs.edit().putString(PREF_SECONDARY_LOCALES_PREFIX + mainLocale.toLanguageTag(), sb.toString()).apply();
}
public static Colors getColorsForCurrentTheme(final Context context, final SharedPreferences prefs) { public static Colors getColorsForCurrentTheme(final Context context, final SharedPreferences prefs) {
boolean isNight = ResourceUtils.isNight(context.getResources()); boolean isNight = ResourceUtils.isNight(context.getResources());
if (SettingsActivity.Companion.getForceOppositeTheme()) isNight = !isNight; if (SettingsActivity.Companion.getForceOppositeTheme()) isNight = !isNight;

View file

@ -28,9 +28,9 @@ import helium314.keyboard.latin.permissions.PermissionsUtil;
import helium314.keyboard.latin.utils.InputTypeUtils; import helium314.keyboard.latin.utils.InputTypeUtils;
import helium314.keyboard.latin.utils.JniUtils; import helium314.keyboard.latin.utils.JniUtils;
import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.Log;
import helium314.keyboard.latin.utils.PopupKeysUtilsKt;
import helium314.keyboard.latin.utils.ScriptUtils; import helium314.keyboard.latin.utils.ScriptUtils;
import helium314.keyboard.latin.utils.SubtypeSettings; import helium314.keyboard.latin.utils.SubtypeSettings;
import helium314.keyboard.latin.utils.SubtypeUtilsKt;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -156,6 +156,7 @@ public class SettingsValues {
@NonNull final InputAttributes inputAttributes) { @NonNull final InputAttributes inputAttributes) {
mLocale = ConfigurationCompatKt.locale(res.getConfiguration()); mLocale = ConfigurationCompatKt.locale(res.getConfiguration());
mDisplayOrientation = res.getConfiguration().orientation; mDisplayOrientation = res.getConfiguration().orientation;
final InputMethodSubtype selectedSubtype = SubtypeSettings.INSTANCE.getSelectedSubtype(prefs);
// Store the input attributes // Store the input attributes
mInputAttributes = inputAttributes; mInputAttributes = inputAttributes;
@ -174,7 +175,7 @@ public class SettingsValues {
mLanguageSwitchKeyToOtherSubtypes = languagePref.equals("internal") || languagePref.equals("both"); mLanguageSwitchKeyToOtherSubtypes = languagePref.equals("internal") || languagePref.equals("both");
mShowsLanguageSwitchKey = prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, Defaults.PREF_SHOW_LANGUAGE_SWITCH_KEY); mShowsLanguageSwitchKey = prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, Defaults.PREF_SHOW_LANGUAGE_SWITCH_KEY);
mShowsNumberRow = prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW, Defaults.PREF_SHOW_NUMBER_ROW); mShowsNumberRow = prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW, Defaults.PREF_SHOW_NUMBER_ROW);
mLocalizedNumberRow = prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, Defaults.PREF_LOCALIZED_NUMBER_ROW); mLocalizedNumberRow = SubtypeUtilsKt.getHasLocalizedNumberRow(selectedSubtype, prefs);
mShowNumberRowHints = prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW_HINTS, Defaults.PREF_SHOW_NUMBER_ROW_HINTS); mShowNumberRowHints = prefs.getBoolean(Settings.PREF_SHOW_NUMBER_ROW_HINTS, Defaults.PREF_SHOW_NUMBER_ROW_HINTS);
mShowsHints = prefs.getBoolean(Settings.PREF_SHOW_HINTS, Defaults.PREF_SHOW_HINTS); mShowsHints = prefs.getBoolean(Settings.PREF_SHOW_HINTS, Defaults.PREF_SHOW_HINTS);
mShowsPopupHints = prefs.getBoolean(Settings.PREF_SHOW_POPUP_HINTS, Defaults.PREF_SHOW_POPUP_HINTS); mShowsPopupHints = prefs.getBoolean(Settings.PREF_SHOW_POPUP_HINTS, Defaults.PREF_SHOW_POPUP_HINTS);
@ -247,19 +248,14 @@ public class SettingsValues {
mOneHandedModeScale = 1 - (1 - baseScale) * extraScale; mOneHandedModeScale = 1 - (1 - baseScale) * extraScale;
} else } else
mOneHandedModeScale = 1f; mOneHandedModeScale = 1f;
final InputMethodSubtype selectedSubtype = SubtypeSettings.INSTANCE.getSelectedSubtype(prefs); mSecondaryLocales = SubtypeUtilsKt.getSecondaryLocales(selectedSubtype.getExtraValue());
mSecondaryLocales = Settings.getSecondaryLocales(prefs, mLocale);
mShowMorePopupKeys = selectedSubtype.isAsciiCapable() mShowMorePopupKeys = selectedSubtype.isAsciiCapable()
? prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS) ? SubtypeUtilsKt.getMoreKeys(selectedSubtype, prefs)
: LocaleKeyboardInfosKt.POPUP_KEYS_NORMAL; : LocaleKeyboardInfosKt.POPUP_KEYS_NORMAL;
mColors = Settings.getColorsForCurrentTheme(context, prefs); mColors = Settings.getColorsForCurrentTheme(context, prefs);
// read locale-specific popup key settings, fall back to global settings mPopupKeyTypes = SubtypeUtilsKt.getPopupKeyTypes(selectedSubtype, prefs);
final String popupKeyTypesDefault = prefs.getString(Settings.PREF_POPUP_KEYS_ORDER, Defaults.PREF_POPUP_KEYS_ORDER); mPopupKeyLabelSources = SubtypeUtilsKt.getPopupKeyLabelSources(selectedSubtype, prefs);
mPopupKeyTypes = PopupKeysUtilsKt.getEnabledPopupKeys(prefs, Settings.PREF_POPUP_KEYS_ORDER + "_" + mLocale.toLanguageTag(), popupKeyTypesDefault);
final String popupKeyLabelDefault = prefs.getString(Settings.PREF_POPUP_KEYS_LABELS_ORDER, Defaults.PREF_POPUP_KEYS_LABELS_ORDER);
mPopupKeyLabelSources = PopupKeysUtilsKt.getEnabledPopupKeys(prefs, Settings.PREF_POPUP_KEYS_LABELS_ORDER + "_" + mLocale.toLanguageTag(), popupKeyLabelDefault);
mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, Defaults.PREF_ADD_TO_PERSONAL_DICTIONARY); mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, Defaults.PREF_ADD_TO_PERSONAL_DICTIONARY);
mUseContactsDictionary = SettingsValues.readUseContactsEnabled(prefs, context); mUseContactsDictionary = SettingsValues.readUseContactsEnabled(prefs, context);
mCustomNavBarColor = prefs.getBoolean(Settings.PREF_NAVBAR_COLOR, Defaults.PREF_NAVBAR_COLOR); mCustomNavBarColor = prefs.getBoolean(Settings.PREF_NAVBAR_COLOR, Defaults.PREF_NAVBAR_COLOR);

View file

@ -30,6 +30,7 @@ import helium314.keyboard.latin.utils.SubtypeSettings;
import helium314.keyboard.latin.utils.SubtypeUtilsKt; import helium314.keyboard.latin.utils.SubtypeUtilsKt;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.TreeSet; import java.util.TreeSet;
@ -117,7 +118,11 @@ public class UserDictionaryListFragment extends SubScreenFragment {
} }
// Secondary language is added only if main language is selected and if system language is not enabled // Secondary language is added only if main language is selected and if system language is not enabled
if (!localeSystemOnly) { if (!localeSystemOnly) {
sortedLocales.addAll(Settings.getSecondaryLocales(prefs, mainLocale)); final List<InputMethodSubtype> enabled = SubtypeSettings.INSTANCE.getEnabledSubtypes(prefs, false);
for (InputMethodSubtype subtype : enabled) {
if (SubtypeUtilsKt.locale(subtype).equals(mainLocale))
sortedLocales.addAll(SubtypeUtilsKt.getSecondaryLocales(subtype.getExtraValue()));
}
} }
} }

View file

@ -105,7 +105,9 @@ fun cleanUnusedMainDicts(context: Context) {
SubtypeSettings.getEnabledSubtypes(prefs).forEach { subtype -> SubtypeSettings.getEnabledSubtypes(prefs).forEach { subtype ->
val locale = subtype.locale() val locale = subtype.locale()
usedLocaleLanguageTags.add(locale.toLanguageTag()) usedLocaleLanguageTags.add(locale.toLanguageTag())
Settings.getSecondaryLocales(prefs, locale).forEach { usedLocaleLanguageTags.add(it.toLanguageTag()) } }
SubtypeSettings.getAdditionalSubtypes().forEach { subtype ->
getSecondaryLocales(subtype.extraValue).forEach { usedLocaleLanguageTags.add(it.toLanguageTag()) }
} }
for (dir in dirs) { for (dir in dirs) {
if (!dir.isDirectory) continue if (!dir.isDirectory) continue

View file

@ -98,9 +98,9 @@ private fun transformLabel(label: String, params: KeyboardParams): String =
} else label } else label
/** returns a list of enabled popup keys for pref [key] */ /** returns a list of enabled popup keys for pref [key] */
fun getEnabledPopupKeys(prefs: SharedPreferences, key: String, defaultSetting: String): List<String> { fun getEnabledPopupKeys(string: String): List<String> {
return prefs.getString(key, defaultSetting)?.split(";")?.mapNotNull { return string.split(Separators.ENTRY).mapNotNull {
val split = it.split(",") val split = it.split(Separators.KV)
if (split.last() == "true") split.first() else null if (split.last() == "true") split.first() else null
} ?: emptyList() }
} }

View file

@ -29,7 +29,7 @@ object SubtypeSettings {
return getDefaultEnabledSubtypes() return getDefaultEnabledSubtypes()
if (fallback && enabledSubtypes.isEmpty()) if (fallback && enabledSubtypes.isEmpty())
return getDefaultEnabledSubtypes() return getDefaultEnabledSubtypes()
return enabledSubtypes return enabledSubtypes.toList()
} }
fun getAllAvailableSubtypes(): List<InputMethodSubtype> { fun getAllAvailableSubtypes(): List<InputMethodSubtype> {
@ -67,20 +67,22 @@ object SubtypeSettings {
} }
} }
/** returns whether subtype was actually removed, does not remove last subtype */ /** @return whether subtype was actually removed */
fun removeEnabledSubtype(prefs: SharedPreferences, subtype: InputMethodSubtype) { fun removeEnabledSubtype(context: Context, subtype: InputMethodSubtype): Boolean {
require(initialized) require(initialized)
removeEnabledSubtype(prefs, subtype.toSettingsSubtype().toPref()) if (!removeEnabledSubtype(context.prefs(), subtype.toSettingsSubtype().toPref())) return false
enabledSubtypes.remove(subtype) if (!enabledSubtypes.remove(subtype)) reloadEnabledSubtypes(context)
RichInputMethodManager.getInstance().refreshSubtypeCaches() else RichInputMethodManager.getInstance().refreshSubtypeCaches()
return true
} }
fun getSelectedSubtype(prefs: SharedPreferences): InputMethodSubtype { fun getSelectedSubtype(prefs: SharedPreferences): InputMethodSubtype {
require(initialized) require(initialized)
val selectedSubtype = prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)!!.toSettingsSubtype() val selectedSubtype = prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)!!.toSettingsSubtype()
val selectedAdditionalSubtype = selectedSubtype.toAdditionalSubtype() if (selectedSubtype.isAdditionalSubtype(prefs)) {
if (selectedAdditionalSubtype != null && additionalSubtypes.contains(selectedAdditionalSubtype)) val selectedAdditionalSubtype = selectedSubtype.toAdditionalSubtype()
return selectedAdditionalSubtype // don't even care whether it's enabled if (selectedAdditionalSubtype != null) return selectedAdditionalSubtype
}
// no additional subtype, must be a resource subtype // no additional subtype, must be a resource subtype
val subtypes = if (prefs.getBoolean(Settings.PREF_USE_SYSTEM_LOCALES, Defaults.PREF_USE_SYSTEM_LOCALES)) getDefaultEnabledSubtypes() val subtypes = if (prefs.getBoolean(Settings.PREF_USE_SYSTEM_LOCALES, Defaults.PREF_USE_SYSTEM_LOCALES)) getDefaultEnabledSubtypes()
else enabledSubtypes else enabledSubtypes
@ -111,10 +113,9 @@ object SubtypeSettings {
return subtype in additionalSubtypes return subtype in additionalSubtypes
} }
fun updateAdditionalSubtypes(subtypes: List<InputMethodSubtype>) { fun getAdditionalSubtypes(): List<InputMethodSubtype> {
additionalSubtypes.clear() require(initialized)
additionalSubtypes.addAll(subtypes) return additionalSubtypes.toList()
RichInputMethodManager.getInstance().refreshSubtypeCaches()
} }
fun reloadSystemLocales(context: Context) { fun reloadSystemLocales(context: Context) {
@ -129,7 +130,7 @@ object SubtypeSettings {
fun getSystemLocales(): List<Locale> { fun getSystemLocales(): List<Locale> {
require(initialized) require(initialized)
return systemLocales return systemLocales.toList()
} }
fun hasMatchingSubtypeForLocale(locale: Locale): Boolean { fun hasMatchingSubtypeForLocale(locale: Locale): Boolean {
@ -139,16 +140,18 @@ object SubtypeSettings {
fun getResourceSubtypesForLocale(locale: Locale): List<InputMethodSubtype> = resourceSubtypesByLocale[locale].orEmpty() fun getResourceSubtypesForLocale(locale: Locale): List<InputMethodSubtype> = resourceSubtypesByLocale[locale].orEmpty()
fun getAvailableSubtypeLocales(): Collection<Locale> { fun getAvailableSubtypeLocales(): List<Locale> {
require(initialized) require(initialized)
return resourceSubtypesByLocale.keys return resourceSubtypesByLocale.keys.toList()
} }
fun reloadEnabledSubtypes(context: Context) { fun reloadEnabledSubtypes(context: Context) {
require(initialized) require(initialized)
enabledSubtypes.clear() enabledSubtypes.clear()
removeInvalidCustomSubtypes(context) removeInvalidCustomSubtypes(context)
loadAdditionalSubtypes(context.prefs())
loadEnabledSubtypes(context) loadEnabledSubtypes(context)
RichInputMethodManager.getInstance().refreshSubtypeCaches()
} }
fun init(context: Context) { fun init(context: Context) {
@ -190,23 +193,24 @@ object SubtypeSettings {
} }
// remove custom subtypes without a layout file // remove custom subtypes without a layout file
private fun removeInvalidCustomSubtypes(context: Context) { // todo: new layout structure! private fun removeInvalidCustomSubtypes(context: Context) {
val prefs = context.prefs() val prefs = context.prefs()
val additionalSubtypes = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!.split(";") val additionalSubtypes = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!.split(Separators.SETS)
val customSubtypeFiles by lazy { LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, context).map { it.name } } val customLayoutFiles by lazy { LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, context).map { it.name } }
val subtypesToRemove = mutableListOf<String>() val subtypesToRemove = mutableListOf<String>()
additionalSubtypes.forEach { additionalSubtypes.forEach {
val name = it.substringAfter(":").substringBefore(":") val name = it.toSettingsSubtype().mainLayoutName() ?: "qwerty"
if (!LayoutUtilsCustom.isCustomLayout(name)) return@forEach if (!LayoutUtilsCustom.isCustomLayout(name)) return@forEach
if (name !in customSubtypeFiles) if (name !in customLayoutFiles)
subtypesToRemove.add(it) subtypesToRemove.add(it)
} }
if (subtypesToRemove.isEmpty()) return if (subtypesToRemove.isEmpty()) return
Log.w(TAG, "removing custom subtypes without files: $subtypesToRemove") Log.w(TAG, "removing custom subtypes without main layout files: $subtypesToRemove")
Settings.writePrefAdditionalSubtypes(prefs, additionalSubtypes.filterNot { it in subtypesToRemove }.joinToString(";")) Settings.writePrefAdditionalSubtypes(prefs, additionalSubtypes.filterNot { it in subtypesToRemove }.joinToString(Separators.SETS))
} }
private fun loadAdditionalSubtypes(prefs: SharedPreferences) { private fun loadAdditionalSubtypes(prefs: SharedPreferences) {
additionalSubtypes.clear()
val additionalSubtypeString = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!! val additionalSubtypeString = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
val subtypes = SubtypeUtilsAdditional.createAdditionalSubtypes(additionalSubtypeString) val subtypes = SubtypeUtilsAdditional.createAdditionalSubtypes(additionalSubtypeString)
additionalSubtypes.addAll(subtypes) additionalSubtypes.addAll(subtypes)
@ -218,10 +222,12 @@ object SubtypeSettings {
val settingsSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!! val settingsSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!
.split(Separators.SETS).filter { it.isNotEmpty() }.map { it.toSettingsSubtype() } .split(Separators.SETS).filter { it.isNotEmpty() }.map { it.toSettingsSubtype() }
for (settingsSubtype in settingsSubtypes) { for (settingsSubtype in settingsSubtypes) {
val additionalSubtype = settingsSubtype.toAdditionalSubtype() if (settingsSubtype.isAdditionalSubtype(prefs)) {
if (additionalSubtype != null && additionalSubtypes.contains(additionalSubtype)) { val additionalSubtype = settingsSubtype.toAdditionalSubtype()
enabledSubtypes.add(additionalSubtype) if (additionalSubtype != null) {
continue enabledSubtypes.add(additionalSubtype)
continue
}
} }
val subtypesForLocale = resourceSubtypesByLocale[settingsSubtype.locale] val subtypesForLocale = resourceSubtypesByLocale[settingsSubtype.locale]
if (subtypesForLocale == null) { if (subtypesForLocale == null) {
@ -249,11 +255,12 @@ object SubtypeSettings {
} }
} }
private fun removeEnabledSubtype(prefs: SharedPreferences, subtypeString: String) { /** @return whether pref was changed */
private fun removeEnabledSubtype(prefs: SharedPreferences, subtypeString: String): Boolean {
val oldSubtypeString = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!! val oldSubtypeString = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, Defaults.PREF_ENABLED_SUBTYPES)!!
val newString = (oldSubtypeString.split(Separators.SETS) - subtypeString).joinToString(Separators.SETS) val newString = (oldSubtypeString.split(Separators.SETS) - subtypeString).joinToString(Separators.SETS)
if (newString == oldSubtypeString) if (newString == oldSubtypeString)
return // already removed return false// already removed
prefs.edit { putString(Settings.PREF_ENABLED_SUBTYPES, newString) } prefs.edit { putString(Settings.PREF_ENABLED_SUBTYPES, newString) }
if (subtypeString == prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)) { if (subtypeString == prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)) {
// switch subtype if the currently used one has been disabled // switch subtype if the currently used one has been disabled
@ -265,6 +272,7 @@ object SubtypeSettings {
KeyboardSwitcher.getInstance().switchToSubtype(nextSubtype) KeyboardSwitcher.getInstance().switchToSubtype(nextSubtype)
} catch (_: Exception) { } // do nothing if RichInputMethodManager isn't initialized } catch (_: Exception) { } // do nothing if RichInputMethodManager isn't initialized
} }
return true
} }
var initialized = false var initialized = false

View file

@ -1,6 +1,7 @@
package helium314.keyboard.latin.utils package helium314.keyboard.latin.utils
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources import android.content.res.Resources
import android.os.Build import android.os.Build
import android.view.inputmethod.InputMethodSubtype import android.view.inputmethod.InputMethodSubtype
@ -11,8 +12,11 @@ import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAY
import helium314.keyboard.latin.common.LocaleUtils import helium314.keyboard.latin.common.LocaleUtils
import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.common.LocaleUtils.constructLocale
import helium314.keyboard.latin.define.DebugFlags import helium314.keyboard.latin.define.DebugFlags
import helium314.keyboard.latin.settings.Defaults
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.LayoutType.Companion.toExtraValue import helium314.keyboard.latin.utils.LayoutType.Companion.toExtraValue
import helium314.keyboard.latin.utils.ScriptUtils.script import helium314.keyboard.latin.utils.ScriptUtils.script
import helium314.keyboard.latin.utils.SettingsSubtype.Companion.getExtraValueOf
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import java.util.Locale import java.util.Locale
@ -72,6 +76,30 @@ fun InputMethodSubtype.displayName(context: Context): String {
return SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(this) return SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(this)
} }
fun getHasLocalizedNumberRow(subtype: InputMethodSubtype, prefs: SharedPreferences): Boolean =
subtype.getExtraValueOf(ExtraValue.LOCALIZED_NUMBER_ROW)?.toBoolean()
?: prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, Defaults.PREF_LOCALIZED_NUMBER_ROW)
fun getPopupKeyTypes(subtype: InputMethodSubtype, prefs: SharedPreferences): List<String> {
val string = subtype.getExtraValueOf(ExtraValue.POPUP_ORDER)
?: prefs.getString(Settings.PREF_POPUP_KEYS_ORDER, Defaults.PREF_POPUP_KEYS_ORDER)!!
return getEnabledPopupKeys(string)
}
fun getPopupKeyLabelSources(subtype: InputMethodSubtype, prefs: SharedPreferences): List<String> {
val string = subtype.getExtraValueOf(ExtraValue.HINT_ORDER)
?: prefs.getString(Settings.PREF_POPUP_KEYS_LABELS_ORDER, Defaults.PREF_POPUP_KEYS_LABELS_ORDER)!!
return getEnabledPopupKeys(string)
}
fun getMoreKeys(subtype: InputMethodSubtype, prefs: SharedPreferences): String =
subtype.getExtraValueOf(ExtraValue.MORE_POPUPS)
?: prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS)!!
fun getSecondaryLocales(extraValues: String): List<Locale> =
extraValues.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)
?.split(Separators.KV)?.map { it.constructLocale() }.orEmpty()
// some kind of intermediate between the string stored in preferences and an InputMethodSubtype // some kind of intermediate between the string stored in preferences and an InputMethodSubtype
data class SettingsSubtype(val locale: Locale, val extraValues: String) { data class SettingsSubtype(val locale: Locale, val extraValues: String) {
@ -110,10 +138,9 @@ data class SettingsSubtype(val locale: Locale, val extraValues: String) {
return copy(extraValues = newValues) return copy(extraValues = newValues)
} }
fun getExtraValueOf(extraValueKey: String): String? = extraValues.split(",") fun getExtraValueOf(extraValueKey: String): String? = extraValues.getExtraValueOf(extraValueKey)
.firstOrNull { it.startsWith("$extraValueKey=") }?.substringAfter("$extraValueKey=")
fun withLayout(type: LayoutType, name: String): SettingsSubtype { fun withLayout(type: LayoutType, name: String): SettingsSubtype {
val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "") val map = LayoutType.getLayoutMap(getExtraValueOf(KEYBOARD_LAYOUT_SET) ?: "")
map[type] = name map[type] = name
return with(KEYBOARD_LAYOUT_SET, map.toExtraValue()) return with(KEYBOARD_LAYOUT_SET, map.toExtraValue())
@ -126,10 +153,17 @@ data class SettingsSubtype(val locale: Locale, val extraValues: String) {
else with(KEYBOARD_LAYOUT_SET, map.toExtraValue()) else with(KEYBOARD_LAYOUT_SET, map.toExtraValue())
} }
fun isAdditionalSubtype(prefs: SharedPreferences) =
prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
.split(Separators.SETS).contains(toPref())
companion object { companion object {
fun String.toSettingsSubtype() = fun String.toSettingsSubtype() =
SettingsSubtype(substringBefore(Separators.SET).constructLocale(), substringAfter(Separators.SET)) SettingsSubtype(substringBefore(Separators.SET).constructLocale(), substringAfter(Separators.SET))
fun String.getExtraValueOf(extraValueKey: String) = split(",")
.firstOrNull { it.startsWith("$extraValueKey=") }?.substringAfter("$extraValueKey=")
/** Creates a SettingsSubtype from the given InputMethodSubtype. /** Creates a SettingsSubtype from the given InputMethodSubtype.
* Will strip some extra values that are set when creating the InputMethodSubtype from SettingsSubtype */ * Will strip some extra values that are set when creating the InputMethodSubtype from SettingsSubtype */
fun InputMethodSubtype.toSettingsSubtype(): SettingsSubtype { fun InputMethodSubtype.toSettingsSubtype(): SettingsSubtype {

View file

@ -1,5 +1,6 @@
package helium314.keyboard.latin.utils package helium314.keyboard.latin.utils
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.view.inputmethod.InputMethodSubtype import android.view.inputmethod.InputMethodSubtype
@ -22,10 +23,11 @@ object SubtypeUtilsAdditional {
// todo: extra value does not contain UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME for custom layout // todo: extra value does not contain UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME for custom layout
// it did contain that key in 2.3, but where was it set? anyway, need to be careful with separators if we want to use it // it did contain that key in 2.3, but where was it set? anyway, need to be careful with separators if we want to use it
// see also todo in SettingsSubtype // see also todo in SettingsSubtype
// todo: the name always contains the layout, but we may just use the original one
fun createAdditionalSubtype(locale: Locale, extraValue: String, isAsciiCapable: Boolean, fun createAdditionalSubtype(locale: Locale, extraValue: String, isAsciiCapable: Boolean,
isEmojiCapable: Boolean): InputMethodSubtype { isEmojiCapable: Boolean): InputMethodSubtype {
val mainLayoutName = LayoutType.getMainLayoutFromExtraValue(extraValue) ?: "qwerty" val mainLayoutName = LayoutType.getMainLayoutFromExtraValue(extraValue) ?: "qwerty"
val nameId = SubtypeLocaleUtils.getSubtypeNameResId(locale, mainLayoutName) val nameId = getNameResId(locale, mainLayoutName)
val fullExtraValue = extraValue + "," + getAdditionalExtraValues(locale, mainLayoutName, isAsciiCapable, isEmojiCapable) val fullExtraValue = extraValue + "," + getAdditionalExtraValues(locale, mainLayoutName, isAsciiCapable, isEmojiCapable)
val subtypeId = getSubtypeId(locale, fullExtraValue, isAsciiCapable) val subtypeId = getSubtypeId(locale, fullExtraValue, isAsciiCapable)
val builder = InputMethodSubtypeBuilder() val builder = InputMethodSubtypeBuilder()
@ -69,23 +71,22 @@ object SubtypeUtilsAdditional {
} }
// updates additional subtypes, enabled subtypes, and selected subtype // updates additional subtypes, enabled subtypes, and selected subtype
fun changeAdditionalSubtype(from: SettingsSubtype, to: SettingsSubtype, prefs: SharedPreferences) { fun changeAdditionalSubtype(from: SettingsSubtype, to: SettingsSubtype, context: Context) {
val prefs = context.prefs()
val new = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!! val new = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
.split(Separators.SETS).mapTo(sortedSetOf()) { .split(Separators.SETS).mapNotNullTo(sortedSetOf()) {
if (it == from.toPref()) to.toPref() else it if (it == from.toPref()) null else it
} } + to.toPref()
prefs.edit().putString(Settings.PREF_ADDITIONAL_SUBTYPES, new.joinToString(Separators.SETS)).apply() 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 fromSubtype = from.toAdditionalSubtype() // will be null if we edit a resource subtype
val toSubtype = to.toAdditionalSubtype() // should never be null val toSubtype = to.toAdditionalSubtype() // should never be null
if (SubtypeSettings.getSelectedSubtype(prefs) == fromSubtype && toSubtype != null) { val selectedSubtype = prefs.getString(Settings.PREF_SELECTED_SUBTYPE, Defaults.PREF_SELECTED_SUBTYPE)!!.toSettingsSubtype()
if (selectedSubtype == from && toSubtype != null) {
SubtypeSettings.setSelectedSubtype(prefs, toSubtype) SubtypeSettings.setSelectedSubtype(prefs, toSubtype)
} }
if (SubtypeSettings.getEnabledSubtypes(prefs, false).contains(fromSubtype)) { if (fromSubtype != null && SubtypeSettings.removeEnabledSubtype(context, fromSubtype) && toSubtype != null) {
if (fromSubtype != null) SubtypeSettings.addEnabledSubtype(prefs, toSubtype)
SubtypeSettings.removeEnabledSubtype(prefs, fromSubtype)
if (toSubtype != null)
SubtypeSettings.addEnabledSubtype(prefs, toSubtype)
} }
} }
@ -101,6 +102,15 @@ object SubtypeUtilsAdditional {
return subtypes.joinToString(Separators.SETS) { it.toSettingsSubtype().toPref() } return subtypes.joinToString(Separators.SETS) { it.toSettingsSubtype().toPref() }
} }
private fun getNameResId(locale: Locale, mainLayoutName: String): Int {
val nameId = SubtypeLocaleUtils.getSubtypeNameResId(locale, mainLayoutName)
if (nameId != SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) return nameId
SubtypeSettings.getResourceSubtypesForLocale(locale).forEach {
if (it.mainLayoutName() == mainLayoutName) return it.nameResId
}
return SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT
}
/** /**
* Returns the subtype ID that is supposed to be compatible between different version of OSes. * Returns the subtype ID that is supposed to be compatible between different version of OSes.
* *

View file

@ -59,15 +59,17 @@ import helium314.keyboard.latin.utils.SubtypeLocaleUtils
import helium314.keyboard.latin.utils.SubtypeSettings import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
import helium314.keyboard.latin.utils.getDictionaryLocales import helium314.keyboard.latin.utils.getDictionaryLocales
import helium314.keyboard.latin.utils.getSecondaryLocales
import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.getStringResourceOrName
import helium314.keyboard.latin.utils.locale
import helium314.keyboard.latin.utils.prefs import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.screens.GetIcon import helium314.keyboard.settings.screens.GetIcon
import java.util.Locale import java.util.Locale
// todo: // todo:
// save when "editing" a resource subtypes is not working // save when "editing" a resource subtypes is not working
// but really needs to, because e.g. a user may just want to add a secondary locale on swedish qwerty+
// settings upgrade to move the override settings to extra values, and actually use them (via getSelectedSubtype, not RichIMM) // settings upgrade to move the override settings to extra values, and actually use them (via getSelectedSubtype, not RichIMM)
// maybe hide some settings initially? list is long, any maybe e.g. more popups doesn't need to be subtype-dependent
@Composable @Composable
fun SubtypeDialog( fun SubtypeDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
@ -90,7 +92,7 @@ fun SubtypeDialog(
neutralButtonText = if (SubtypeSettings.isAdditionalSubtype(subtype)) null else stringResource(R.string.delete), neutralButtonText = if (SubtypeSettings.isAdditionalSubtype(subtype)) null else stringResource(R.string.delete),
onNeutral = { onNeutral = {
SubtypeUtilsAdditional.removeAdditionalSubtype(prefs, subtype) SubtypeUtilsAdditional.removeAdditionalSubtype(prefs, subtype)
SubtypeSettings.removeEnabledSubtype(prefs, subtype) SubtypeSettings.removeEnabledSubtype(ctx, subtype)
}, // maybe confirm dialog? }, // maybe confirm dialog?
title = { Text(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)) }, title = { Text(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)) },
@ -103,8 +105,8 @@ fun SubtypeDialog(
val appLayouts = LayoutUtils.getAvailableLayouts(LayoutType.MAIN, ctx, currentSubtype.locale) val appLayouts = LayoutUtils.getAvailableLayouts(LayoutType.MAIN, ctx, currentSubtype.locale)
val customLayouts = LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, ctx, currentSubtype.locale).map { it.name } val customLayouts = LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, ctx, currentSubtype.locale).map { it.name }
DropDownField( DropDownField(
items = appLayouts + customLayouts, items = appLayouts + customLayouts, // todo: allow the "+" layouts for languages that have one
selectedItem = currentSubtype.mainLayoutName() ?: "qwerty", // todo: what about qwerty+ and similar? selectedItem = currentSubtype.mainLayoutName() ?: "qwerty",
onSelected = { onSelected = {
currentSubtype = currentSubtype.withLayout(LayoutType.MAIN, it) currentSubtype = currentSubtype.withLayout(LayoutType.MAIN, it)
} }
@ -114,13 +116,14 @@ fun SubtypeDialog(
// yes, even just to make clear what is custom // yes, even just to make clear what is custom
} }
} }
WithSmallTitle(stringResource(R.string.secondary_locale)) { if (availableLocalesForScript.size > 1) {
TextButton(onClick = { showSecondaryLocaleDialog = true }) { WithSmallTitle(stringResource(R.string.secondary_locale)) {
val text = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES) TextButton(onClick = { showSecondaryLocaleDialog = true }) {
?.split(Separators.KV)?.joinToString(", ") { val text = getSecondaryLocales(currentSubtype.extraValues).joinToString(", ") {
LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx) LocaleUtils.getLocaleDisplayNameInSystemLocale(it, ctx)
} ?: stringResource(R.string.action_none) }.ifEmpty { stringResource(R.string.action_none) }
Text(text, Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge) Text(text, Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge)
}
} }
} }
WithSmallTitle("dictionaries") { WithSmallTitle("dictionaries") {

View file

@ -177,8 +177,6 @@ fun BackupRestorePreference(setting: Setting) {
wait.await() wait.await()
checkVersionUpgrade(ctx) checkVersionUpgrade(ctx)
Settings.getInstance().startListener() Settings.getInstance().startListener()
val additionalSubtypes = prefs.getString(Settings.PREF_ADDITIONAL_SUBTYPES, Defaults.PREF_ADDITIONAL_SUBTYPES)!!
SubtypeSettings.updateAdditionalSubtypes(SubtypeUtilsAdditional.createAdditionalSubtypes(additionalSubtypes))
SubtypeSettings.reloadEnabledSubtypes(ctx) SubtypeSettings.reloadEnabledSubtypes(ctx)
val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION) val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
ctx.getActivity()?.sendBroadcast(newDictBroadcast) ctx.getActivity()?.sendBroadcast(newDictBroadcast)

View file

@ -84,7 +84,7 @@ fun LanguageScreen(
Text(item.displayName(ctx), style = MaterialTheme.typography.bodyLarge) Text(item.displayName(ctx), style = MaterialTheme.typography.bodyLarge)
val description = item.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)?.split(Separators.KV) val description = item.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)?.split(Separators.KV)
?.joinToString(", ") { LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx) } ?.joinToString(", ") { LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx) }
if (description != null) if (description != null) // todo: description should clarify when it's a default subtype that can't be changed / will be cloned
Text( Text(
text = description, text = description,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
@ -95,7 +95,7 @@ fun LanguageScreen(
checked = item in SubtypeSettings.getEnabledSubtypes(prefs), checked = item in SubtypeSettings.getEnabledSubtypes(prefs),
onCheckedChange = { onCheckedChange = {
if (it) SubtypeSettings.addEnabledSubtype(prefs, item) if (it) SubtypeSettings.addEnabledSubtype(prefs, item)
else SubtypeSettings.removeEnabledSubtype(prefs, item) else SubtypeSettings.removeEnabledSubtype(ctx, item)
} }
) )
} }
@ -106,8 +106,9 @@ fun LanguageScreen(
SubtypeDialog( SubtypeDialog(
onDismissRequest = { selectedSubtype = null }, onDismissRequest = { selectedSubtype = null },
onConfirmed = { onConfirmed = {
// todo: this does not work when "modifying" a resource subtype // todo: this does not work when "modifying" a resource subtype like German (Germany) or Danish (probably because of the + layout)
SubtypeUtilsAdditional.changeAdditionalSubtype(oldSubtype.toSettingsSubtype(), it, prefs) // maybe this also applied to upgrading
SubtypeUtilsAdditional.changeAdditionalSubtype(oldSubtype.toSettingsSubtype(), it, ctx)
sortedSubtypes = getSortedSubtypes(ctx) sortedSubtypes = getSortedSubtypes(ctx)
}, },
subtype = oldSubtype subtype = oldSubtype