mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-05 14:20:20 +00:00
move SubtypeLocaleUtils to Kotlin
This commit is contained in:
parent
175b5ea197
commit
82e6d8a5cb
2 changed files with 264 additions and 318 deletions
|
@ -1,318 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
* modified
|
|
||||||
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package helium314.keyboard.latin.utils;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.view.inputmethod.InputMethodSubtype;
|
|
||||||
|
|
||||||
import helium314.keyboard.compat.ConfigurationCompatKt;
|
|
||||||
import helium314.keyboard.latin.R;
|
|
||||||
import helium314.keyboard.latin.common.Constants;
|
|
||||||
import helium314.keyboard.latin.common.LocaleUtils;
|
|
||||||
import helium314.keyboard.latin.common.StringUtils;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import static helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.COMBINING_RULES;
|
|
||||||
import static helium314.keyboard.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper class to deal with subtype locales.
|
|
||||||
*/
|
|
||||||
// TODO: consolidate this into RichInputMethodSubtype
|
|
||||||
// todo (later): see whether this complicated mess can be simplified
|
|
||||||
public final class SubtypeLocaleUtils {
|
|
||||||
static final String TAG = SubtypeLocaleUtils.class.getSimpleName();
|
|
||||||
|
|
||||||
// This reference class {@link R} must be located in the same package as LatinIME.java.
|
|
||||||
// switched to context.getPackageName(), which works with changed debug package name
|
|
||||||
// any reason to prefer original version?
|
|
||||||
// private static final String RESOURCE_PACKAGE_NAME = R.class.getPackage().getName();
|
|
||||||
|
|
||||||
// Special language code to represent "no language".
|
|
||||||
public static final String NO_LANGUAGE = "zz";
|
|
||||||
public static final String QWERTY = "qwerty";
|
|
||||||
public static final String EMOJI = "emoji";
|
|
||||||
public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic;
|
|
||||||
|
|
||||||
private static volatile boolean sInitialized = false;
|
|
||||||
private static final Object sInitializeLock = new Object();
|
|
||||||
private static Resources sResources;
|
|
||||||
// Keyboard layout to its display name map.
|
|
||||||
private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>();
|
|
||||||
// Keyboard layout to subtype name resource id map.
|
|
||||||
private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>();
|
|
||||||
// Exceptional locale whose name should be displayed in Locale.ROOT.
|
|
||||||
private static final HashMap<String, Integer> sExceptionalLocaleDisplayedInRootLocale = new HashMap<>();
|
|
||||||
// Exceptional locale to subtype name resource id map.
|
|
||||||
private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>();
|
|
||||||
// Exceptional locale to subtype name with layout resource id map.
|
|
||||||
private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap = new HashMap<>();
|
|
||||||
private static final HashMap<Integer, String> sResourceSubtypeDisplayNames = new HashMap<>();
|
|
||||||
private static final String SUBTYPE_NAME_RESOURCE_PREFIX = "string/subtype_";
|
|
||||||
private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = "string/subtype_generic_";
|
|
||||||
private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX = "string/subtype_with_layout_";
|
|
||||||
private static final String SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX = "string/subtype_no_language_";
|
|
||||||
private static final String SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX = "string/subtype_in_root_locale_";
|
|
||||||
|
|
||||||
private SubtypeLocaleUtils() {
|
|
||||||
// Intentional empty constructor for utility class.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this initialization method can be called multiple times.
|
|
||||||
public static void init(final Context context) {
|
|
||||||
synchronized (sInitializeLock) {
|
|
||||||
if (!sInitialized) {
|
|
||||||
initLocked(context);
|
|
||||||
sInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void initLocked(final Context context) {
|
|
||||||
final String RESOURCE_PACKAGE_NAME = context.getPackageName();
|
|
||||||
final Resources res = context.getResources();
|
|
||||||
sResources = res;
|
|
||||||
|
|
||||||
final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
|
|
||||||
final String[] layoutDisplayNames = res.getStringArray(R.array.predefined_layout_display_names);
|
|
||||||
for (int i = 0; i < predefinedLayoutSet.length; i++) {
|
|
||||||
final String layoutName = predefinedLayoutSet[i];
|
|
||||||
sKeyboardLayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]);
|
|
||||||
final String resourceName = SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX + layoutName;
|
|
||||||
final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
|
|
||||||
sKeyboardLayoutToNameIdsMap.put(layoutName, resId);
|
|
||||||
// Register subtype name resource id of "No language" with key "zz_<layout>"
|
|
||||||
final String noLanguageResName = SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX + layoutName;
|
|
||||||
final int noLanguageResId = res.getIdentifier(noLanguageResName, null, RESOURCE_PACKAGE_NAME);
|
|
||||||
final String key = getNoLanguageLayoutKey(layoutName);
|
|
||||||
sKeyboardLayoutToNameIdsMap.put(key, noLanguageResId);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String[] exceptionalLocaleInRootLocale = res.getStringArray(R.array.subtype_locale_displayed_in_root_locale);
|
|
||||||
for (final String languageTag : exceptionalLocaleInRootLocale) {
|
|
||||||
final String resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + languageTag.replace('-', '_');
|
|
||||||
final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
|
|
||||||
sExceptionalLocaleDisplayedInRootLocale.put(languageTag, resId);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String[] exceptionalLocales = res.getStringArray(R.array.subtype_locale_exception_keys);
|
|
||||||
for (final String languageTag : exceptionalLocales) {
|
|
||||||
final String resourceName = SUBTYPE_NAME_RESOURCE_PREFIX + languageTag.replace('-', '_');
|
|
||||||
final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
|
|
||||||
sExceptionalLocaleToNameIdsMap.put(languageTag, resId);
|
|
||||||
final String resourceNameWithLayout = SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + languageTag.replace('-', '_');
|
|
||||||
final int resIdWithLayout = res.getIdentifier(resourceNameWithLayout, null, RESOURCE_PACKAGE_NAME);
|
|
||||||
sExceptionalLocaleToWithLayoutNameIdsMap.put(languageTag, resIdWithLayout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isExceptionalLocale(final Locale locale) {
|
|
||||||
return sExceptionalLocaleToNameIdsMap.containsKey(locale.toLanguageTag());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getNoLanguageLayoutKey(final String keyboardLayoutName) {
|
|
||||||
return NO_LANGUAGE + "_" + keyboardLayoutName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getSubtypeNameResId(final Locale locale, final String keyboardLayoutName) {
|
|
||||||
final String languageTag = locale.toLanguageTag();
|
|
||||||
if (isExceptionalLocale(locale)) {
|
|
||||||
return sExceptionalLocaleToWithLayoutNameIdsMap.get(languageTag);
|
|
||||||
}
|
|
||||||
final String key = NO_LANGUAGE.equals(languageTag)
|
|
||||||
? getNoLanguageLayoutKey(keyboardLayoutName)
|
|
||||||
: keyboardLayoutName;
|
|
||||||
final Integer nameId = sKeyboardLayoutToNameIdsMap.get(key);
|
|
||||||
return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static Locale getDisplayLocaleOfSubtypeLocale(@NonNull final Locale locale) {
|
|
||||||
final String languageTag = locale.toLanguageTag();
|
|
||||||
if (NO_LANGUAGE.equals(languageTag)) {
|
|
||||||
return ConfigurationCompatKt.locale(sResources.getConfiguration());
|
|
||||||
}
|
|
||||||
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(languageTag)) {
|
|
||||||
return Locale.ROOT;
|
|
||||||
}
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getSubtypeLocaleDisplayNameInSystemLocale(@NonNull final Locale locale) {
|
|
||||||
final Locale displayLocale = ConfigurationCompatKt.locale(sResources.getConfiguration());
|
|
||||||
return getSubtypeLocaleDisplayNameInternal(locale, displayLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getSubtypeLocaleDisplayName(@NonNull final Locale locale) {
|
|
||||||
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(locale);
|
|
||||||
return getSubtypeLocaleDisplayNameInternal(locale, displayLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getSubtypeLanguageDisplayName(@NonNull final Locale locale) {
|
|
||||||
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(locale);
|
|
||||||
final Locale languageLocale;
|
|
||||||
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(locale.toLanguageTag())) {
|
|
||||||
languageLocale = locale;
|
|
||||||
} else {
|
|
||||||
languageLocale = LocaleUtils.constructLocale(locale.getLanguage());
|
|
||||||
}
|
|
||||||
return getSubtypeLocaleDisplayNameInternal(languageLocale, displayLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static String getSubtypeLocaleDisplayNameInternal(@NonNull final Locale locale,
|
|
||||||
@NonNull final Locale displayLocale) {
|
|
||||||
final String languageTag = locale.toLanguageTag();
|
|
||||||
if (NO_LANGUAGE.equals(locale.toLanguageTag())) {
|
|
||||||
// No language subtype should be displayed in system locale.
|
|
||||||
return sResources.getString(R.string.subtype_no_language);
|
|
||||||
}
|
|
||||||
final Integer exceptionalNameResId;
|
|
||||||
if (displayLocale.equals(Locale.ROOT)
|
|
||||||
&& sExceptionalLocaleDisplayedInRootLocale.containsKey(languageTag)) {
|
|
||||||
exceptionalNameResId = sExceptionalLocaleDisplayedInRootLocale.get(languageTag);
|
|
||||||
} else if (sExceptionalLocaleToNameIdsMap.containsKey(languageTag)) {
|
|
||||||
exceptionalNameResId = sExceptionalLocaleToNameIdsMap.get(languageTag);
|
|
||||||
} else {
|
|
||||||
exceptionalNameResId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String displayName;
|
|
||||||
if (exceptionalNameResId != null) {
|
|
||||||
displayName = RunInLocaleKt.runInLocale(sResources, displayLocale, res -> res.getString(exceptionalNameResId));
|
|
||||||
} else {
|
|
||||||
displayName = LocaleUtils.getLocaleDisplayNameInLocale(locale, sResources, displayLocale);
|
|
||||||
}
|
|
||||||
return StringUtils.capitalizeFirstCodePoint(displayName, displayLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputMethodSubtype's display name in its locale.
|
|
||||||
// isAdditionalSubtype (T=true, F=false)
|
|
||||||
// locale layout | display name
|
|
||||||
// ------ ------- - ----------------------
|
|
||||||
// en_US qwerty F English (US) exception
|
|
||||||
// en_GB qwerty F English (UK) exception
|
|
||||||
// es_US spanish F Español (EE.UU.) exception
|
|
||||||
// fr azerty F Français
|
|
||||||
// fr_CA qwerty F Français (Canada)
|
|
||||||
// fr_CH swiss F Français (Suisse)
|
|
||||||
// de qwertz F Deutsch
|
|
||||||
// de_CH swiss T Deutsch (Schweiz)
|
|
||||||
// zz qwerty F Alphabet (QWERTY) in system locale
|
|
||||||
// fr qwertz T Français (QWERTZ)
|
|
||||||
// de qwerty T Deutsch (QWERTY)
|
|
||||||
// en_US azerty T English (US) (AZERTY) exception
|
|
||||||
// zz azerty T Alphabet (AZERTY) in system locale
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static String getReplacementString(@NonNull final InputMethodSubtype subtype,
|
|
||||||
@NonNull final Locale displayLocale) {
|
|
||||||
if (subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
|
|
||||||
return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
|
|
||||||
}
|
|
||||||
return getSubtypeLocaleDisplayNameInternal(SubtypeUtilsKt.locale(subtype), displayLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getDisplayNameInSystemLocale(@NonNull final String mainLayoutName, @NonNull final Locale locale) {
|
|
||||||
final String displayName = getMainLayoutDisplayName(mainLayoutName);
|
|
||||||
if (displayName != null) // works for custom and latin layouts
|
|
||||||
return displayName;
|
|
||||||
// we have some locale-specific layout
|
|
||||||
for (InputMethodSubtype subtype : SubtypeSettings.INSTANCE.getResourceSubtypesForLocale(locale)) {
|
|
||||||
final String main = LayoutType.Companion.getMainLayoutFromExtraValue(subtype.getExtraValue());
|
|
||||||
if (mainLayoutName.equals(main))
|
|
||||||
return getSubtypeDisplayNameInSystemLocale(subtype);
|
|
||||||
}
|
|
||||||
return mainLayoutName; // should never happen...
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getSubtypeDisplayNameInSystemLocale(@NonNull final InputMethodSubtype subtype) {
|
|
||||||
final String cached = sResourceSubtypeDisplayNames.get(subtype.hashCode());
|
|
||||||
if (cached != null) return cached;
|
|
||||||
|
|
||||||
final Locale displayLocale = ConfigurationCompatKt.locale(sResources.getConfiguration());
|
|
||||||
final String displayName = getSubtypeDisplayNameInternal(subtype, displayLocale);
|
|
||||||
|
|
||||||
if (!subtype.containsExtraValueKey(Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE)) {
|
|
||||||
sResourceSubtypeDisplayNames.put(subtype.hashCode(), displayName);
|
|
||||||
}
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearDisplayNameCache() {
|
|
||||||
sResourceSubtypeDisplayNames.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getSubtypeNameForLogging(@Nullable final InputMethodSubtype subtype) {
|
|
||||||
if (subtype == null) {
|
|
||||||
return "<null subtype>";
|
|
||||||
}
|
|
||||||
return SubtypeUtilsKt.locale(subtype) + "/" + getMainLayoutName(subtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static String getSubtypeDisplayNameInternal(@NonNull final InputMethodSubtype subtype,
|
|
||||||
@NonNull final Locale displayLocale) {
|
|
||||||
final String replacementString = getReplacementString(subtype, displayLocale);
|
|
||||||
final int nameResId = subtype.getNameResId();
|
|
||||||
return RunInLocaleKt.runInLocale(sResources, displayLocale,
|
|
||||||
res -> {
|
|
||||||
try {
|
|
||||||
return StringUtils.capitalizeFirstCodePoint(res.getString(nameResId, replacementString), displayLocale);
|
|
||||||
} catch (Resources.NotFoundException e) {
|
|
||||||
Log.w(TAG, "Unknown subtype: mode=" + subtype.getMode()
|
|
||||||
+ " nameResId=" + subtype.getNameResId()
|
|
||||||
+ " locale=" + subtype.getLocale()
|
|
||||||
+ " extra=" + subtype.getExtraValue()
|
|
||||||
+ "\n" + DebugLogUtils.getStackTrace());
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static String getMainLayoutDisplayName(@NonNull final InputMethodSubtype subtype) {
|
|
||||||
final String layoutName = getMainLayoutName(subtype);
|
|
||||||
return getMainLayoutDisplayName(layoutName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static String getMainLayoutDisplayName(@NonNull final String layoutName) {
|
|
||||||
if (LayoutUtilsCustom.INSTANCE.isCustomLayout(layoutName))
|
|
||||||
return LayoutUtilsCustom.INSTANCE.getDisplayName(layoutName);
|
|
||||||
return sKeyboardLayoutToDisplayNameMap.get(layoutName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static String getMainLayoutName(final InputMethodSubtype subtype) {
|
|
||||||
String mainLayoutName = SubtypeUtilsKt.mainLayoutName(subtype);
|
|
||||||
if (mainLayoutName == null && subtype.isAsciiCapable()) {
|
|
||||||
mainLayoutName = QWERTY;
|
|
||||||
}
|
|
||||||
if (mainLayoutName == null) { // we could search for a subtype with the correct script, but this is a bug anyway...
|
|
||||||
Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " +
|
|
||||||
"locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue());
|
|
||||||
return QWERTY;
|
|
||||||
}
|
|
||||||
return mainLayoutName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) {
|
|
||||||
return subtype.getExtraValueOf(COMBINING_RULES);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
* modified
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||||
|
*/
|
||||||
|
package helium314.keyboard.latin.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.view.inputmethod.InputMethodSubtype
|
||||||
|
import helium314.keyboard.compat.locale
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
|
import helium314.keyboard.latin.common.Constants.Subtype.ExtraValue
|
||||||
|
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
|
||||||
|
import helium314.keyboard.latin.common.LocaleUtils.getLocaleDisplayNameInLocale
|
||||||
|
import helium314.keyboard.latin.common.StringUtils
|
||||||
|
import helium314.keyboard.latin.utils.LayoutType.Companion.getMainLayoutFromExtraValue
|
||||||
|
import helium314.keyboard.latin.utils.LayoutUtilsCustom.getDisplayName
|
||||||
|
import helium314.keyboard.latin.utils.LayoutUtilsCustom.isCustomLayout
|
||||||
|
import helium314.keyboard.latin.utils.SubtypeSettings.getResourceSubtypesForLocale
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.concurrent.Volatile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class to deal with subtype locales.
|
||||||
|
*/
|
||||||
|
object SubtypeLocaleUtils {
|
||||||
|
@Volatile
|
||||||
|
private var initialized = false
|
||||||
|
private lateinit var resources: Resources
|
||||||
|
|
||||||
|
// Keyboard layout to its display name map.
|
||||||
|
private val keyboardLayoutToDisplayName = HashMap<String, String>()
|
||||||
|
|
||||||
|
// Keyboard layout to subtype name resource id map.
|
||||||
|
private val keyboardLayoutToNameIds = HashMap<String, Int>()
|
||||||
|
|
||||||
|
// Exceptional locale whose name should be displayed in Locale.ROOT.
|
||||||
|
private val exceptionalLocaleDisplayedInRootLocale = HashMap<String, Int>()
|
||||||
|
|
||||||
|
// Exceptional locale to subtype name resource id map.
|
||||||
|
private val exceptionalLocaleToNameIds = HashMap<String, Int>()
|
||||||
|
|
||||||
|
// Exceptional locale to subtype name with layout resource id map.
|
||||||
|
private val exceptionalLocaleToWithLayoutNameIds = HashMap<String, Int>()
|
||||||
|
private val resourceSubtypeDisplayNames = HashMap<Int, String>()
|
||||||
|
|
||||||
|
// Note that this initialization method can be called multiple times.
|
||||||
|
@JvmStatic
|
||||||
|
fun init(context: Context) {
|
||||||
|
synchronized(this) {
|
||||||
|
if (!initialized) {
|
||||||
|
initLocked(context)
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initLocked(context: Context) {
|
||||||
|
val packageName = context.packageName
|
||||||
|
resources = context.resources
|
||||||
|
|
||||||
|
// todo: layout names are currently translatable in subtype_no_language_* but not the default names
|
||||||
|
// just remove the separate "alphabet (<layout>)" strings and have a single "alphabet (%s)"?
|
||||||
|
// or rather use the same style as for languages and only have "alphabet"
|
||||||
|
val predefinedLayouts = resources.getStringArray(R.array.predefined_layouts)
|
||||||
|
val layoutDisplayNames = resources.getStringArray(R.array.predefined_layout_display_names)
|
||||||
|
for (i in predefinedLayouts.indices) {
|
||||||
|
val layoutName = predefinedLayouts[i]
|
||||||
|
keyboardLayoutToDisplayName[layoutName] = layoutDisplayNames[i]
|
||||||
|
val resourceName = SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX + layoutName
|
||||||
|
val resId = resources.getIdentifier(resourceName, null, packageName)
|
||||||
|
keyboardLayoutToNameIds[layoutName] = resId
|
||||||
|
// Register subtype name resource id of "No language" with key "zz_<layout>"
|
||||||
|
val noLanguageResName = SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX + layoutName
|
||||||
|
val noLanguageResId = resources.getIdentifier(noLanguageResName, null, packageName)
|
||||||
|
val key = getNoLanguageLayoutKey(layoutName)
|
||||||
|
keyboardLayoutToNameIds[key] = noLanguageResId
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: do it using 2 arrays like predefined_layouts (and adjust information in layouts.md)
|
||||||
|
val exceptionalLocaleInRootLocale = resources.getStringArray(R.array.subtype_locale_displayed_in_root_locale)
|
||||||
|
for (languageTag in exceptionalLocaleInRootLocale) {
|
||||||
|
val resourceName = SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX + languageTag.replace('-', '_')
|
||||||
|
val resId = resources.getIdentifier(resourceName, null, packageName)
|
||||||
|
exceptionalLocaleDisplayedInRootLocale[languageTag] = resId
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: do it using 2 arrays like predefined_layouts (and adjust information in layouts.md)
|
||||||
|
// and the _with_layout variants can be removed?
|
||||||
|
val exceptionalLocales = resources.getStringArray(R.array.subtype_locale_exception_keys)
|
||||||
|
for (languageTag in exceptionalLocales) {
|
||||||
|
val resourceName = SUBTYPE_NAME_RESOURCE_PREFIX + languageTag.replace('-', '_')
|
||||||
|
val resId = resources.getIdentifier(resourceName, null, packageName)
|
||||||
|
exceptionalLocaleToNameIds[languageTag] = resId
|
||||||
|
val resourceNameWithLayout = SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX + languageTag.replace('-', '_')
|
||||||
|
val resIdWithLayout = resources.getIdentifier(resourceNameWithLayout, null, packageName)
|
||||||
|
exceptionalLocaleToWithLayoutNameIds[languageTag] = resIdWithLayout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isExceptionalLocale(locale: Locale): Boolean {
|
||||||
|
return exceptionalLocaleToNameIds.containsKey(locale.toLanguageTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNoLanguageLayoutKey(keyboardLayoutName: String): String {
|
||||||
|
return NO_LANGUAGE + "_" + keyboardLayoutName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSubtypeNameResId(locale: Locale, keyboardLayoutName: String): Int {
|
||||||
|
val languageTag = locale.toLanguageTag()
|
||||||
|
if (isExceptionalLocale(locale)) {
|
||||||
|
return exceptionalLocaleToWithLayoutNameIds[languageTag]!!
|
||||||
|
}
|
||||||
|
val key = if (languageTag == NO_LANGUAGE) getNoLanguageLayoutKey(keyboardLayoutName)
|
||||||
|
else keyboardLayoutName
|
||||||
|
return keyboardLayoutToNameIds[key] ?: UNKNOWN_KEYBOARD_LAYOUT
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDisplayLocaleOfSubtypeLocale(locale: Locale): Locale {
|
||||||
|
val languageTag = locale.toLanguageTag()
|
||||||
|
if (languageTag == NO_LANGUAGE)
|
||||||
|
return resources.configuration.locale()
|
||||||
|
if (exceptionalLocaleDisplayedInRootLocale.containsKey(languageTag))
|
||||||
|
return Locale.ROOT
|
||||||
|
return locale
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSubtypeLocaleDisplayNameInSystemLocale(locale: Locale): String {
|
||||||
|
val displayLocale = resources.configuration.locale()
|
||||||
|
return getSubtypeLocaleDisplayNameInternal(locale, displayLocale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSubtypeLocaleDisplayName(locale: Locale): String {
|
||||||
|
val displayLocale = getDisplayLocaleOfSubtypeLocale(locale)
|
||||||
|
return getSubtypeLocaleDisplayNameInternal(locale, displayLocale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSubtypeLanguageDisplayName(locale: Locale): String {
|
||||||
|
val languageLocale = if (exceptionalLocaleDisplayedInRootLocale.containsKey(locale.toLanguageTag()))
|
||||||
|
locale
|
||||||
|
else
|
||||||
|
locale.language.constructLocale()
|
||||||
|
return getSubtypeLocaleDisplayNameInternal(languageLocale, getDisplayLocaleOfSubtypeLocale(locale))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSubtypeLocaleDisplayNameInternal(locale: Locale, displayLocale: Locale): String {
|
||||||
|
val languageTag = locale.toLanguageTag()
|
||||||
|
if (languageTag == NO_LANGUAGE) {
|
||||||
|
// "No language" subtype should be displayed in system locale.
|
||||||
|
return resources.getString(R.string.subtype_no_language)
|
||||||
|
}
|
||||||
|
val exceptionalNameResId = if (displayLocale == Locale.ROOT
|
||||||
|
&& exceptionalLocaleDisplayedInRootLocale.containsKey(languageTag)
|
||||||
|
)
|
||||||
|
exceptionalLocaleDisplayedInRootLocale[languageTag]
|
||||||
|
else
|
||||||
|
exceptionalLocaleToNameIds[languageTag]
|
||||||
|
val displayName = if (exceptionalNameResId != null) {
|
||||||
|
runInLocale(resources, displayLocale) { res: Resources -> res.getString(exceptionalNameResId) }
|
||||||
|
} else {
|
||||||
|
getLocaleDisplayNameInLocale(locale, resources, displayLocale)
|
||||||
|
}
|
||||||
|
return StringUtils.capitalizeFirstCodePoint(displayName, displayLocale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputMethodSubtype's display name in its locale.
|
||||||
|
// isAdditionalSubtype (T=true, F=false)
|
||||||
|
// locale layout | display name
|
||||||
|
// ------ ------- - ----------------------
|
||||||
|
// en_US qwerty F English (US) exception
|
||||||
|
// en_GB qwerty F English (UK) exception
|
||||||
|
// es_US spanish F Español (EE.UU.) exception
|
||||||
|
// fr azerty F Français
|
||||||
|
// fr_CA qwerty F Français (Canada)
|
||||||
|
// fr_CH swiss F Français (Suisse)
|
||||||
|
// de qwertz F Deutsch
|
||||||
|
// de_CH swiss T Deutsch (Schweiz)
|
||||||
|
// zz qwerty F Alphabet (QWERTY) in system locale
|
||||||
|
// fr qwertz T Français (QWERTZ)
|
||||||
|
// de qwerty T Deutsch (QWERTY)
|
||||||
|
// en_US azerty T English (US) (AZERTY) exception
|
||||||
|
// zz azerty T Alphabet (AZERTY) in system locale
|
||||||
|
private fun getReplacementString(subtype: InputMethodSubtype, displayLocale: Locale): String =
|
||||||
|
subtype.getExtraValueOf(ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
|
||||||
|
?: getSubtypeLocaleDisplayNameInternal(subtype.locale(), displayLocale)
|
||||||
|
|
||||||
|
fun getDisplayNameInSystemLocale(mainLayoutName: String, locale: Locale): String {
|
||||||
|
getMainLayoutDisplayName(mainLayoutName)?.let { return it } // works for custom and latin layouts
|
||||||
|
|
||||||
|
// we have some locale-specific layout
|
||||||
|
for (subtype in getResourceSubtypesForLocale(locale)) {
|
||||||
|
if (mainLayoutName == getMainLayoutFromExtraValue(subtype.extraValue))
|
||||||
|
return getSubtypeDisplayNameInSystemLocale(subtype)
|
||||||
|
}
|
||||||
|
return mainLayoutName // should never happen...
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSubtypeDisplayNameInSystemLocale(subtype: InputMethodSubtype): String {
|
||||||
|
resourceSubtypeDisplayNames[subtype.hashCode()]?.let { return it }
|
||||||
|
|
||||||
|
val displayName = getSubtypeDisplayNameInternal(subtype, resources.configuration.locale())
|
||||||
|
if (!subtype.containsExtraValueKey(ExtraValue.IS_ADDITIONAL_SUBTYPE)) {
|
||||||
|
resourceSubtypeDisplayNames[subtype.hashCode()] = displayName
|
||||||
|
}
|
||||||
|
return displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun clearDisplayNameCache() {
|
||||||
|
resourceSubtypeDisplayNames.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getSubtypeNameForLogging(subtype: InputMethodSubtype?): String {
|
||||||
|
if (subtype == null) {
|
||||||
|
return "<null subtype>"
|
||||||
|
}
|
||||||
|
return subtype.locale().toString() + "/" + getMainLayoutName(subtype)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSubtypeDisplayNameInternal(subtype: InputMethodSubtype, displayLocale: Locale): String {
|
||||||
|
val replacementString = getReplacementString(subtype, displayLocale)
|
||||||
|
return runInLocale(resources, displayLocale) { res: Resources ->
|
||||||
|
try {
|
||||||
|
StringUtils.capitalizeFirstCodePoint(res.getString(subtype.nameResId, replacementString), displayLocale)
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
Log.w(TAG, ("Unknown subtype: mode=${subtype.mode} nameResId=${subtype.nameResId} locale=${subtype.locale()} extra=${subtype.extraValue}\n${DebugLogUtils.getStackTrace()}"))
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMainLayoutDisplayName(subtype: InputMethodSubtype): String? =
|
||||||
|
getMainLayoutDisplayName(getMainLayoutName(subtype))
|
||||||
|
|
||||||
|
fun getMainLayoutDisplayName(layoutName: String): String? =
|
||||||
|
if (isCustomLayout(layoutName)) getDisplayName(layoutName)
|
||||||
|
else keyboardLayoutToDisplayName[layoutName]
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getMainLayoutName(subtype: InputMethodSubtype): String {
|
||||||
|
subtype.mainLayoutName()?.let { return it }
|
||||||
|
if (!subtype.isAsciiCapable)
|
||||||
|
Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: locale=${subtype.locale()} extraValue=${subtype.extraValue}")
|
||||||
|
return QWERTY
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getCombiningRulesExtraValue(subtype: InputMethodSubtype): String = subtype.getExtraValueOf(ExtraValue.COMBINING_RULES)
|
||||||
|
|
||||||
|
// Special language code to represent "no language".
|
||||||
|
const val NO_LANGUAGE = "zz"
|
||||||
|
const val QWERTY = "qwerty"
|
||||||
|
const val EMOJI = "emoji"
|
||||||
|
val UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic
|
||||||
|
|
||||||
|
private val TAG = SubtypeLocaleUtils::class.java.simpleName
|
||||||
|
private const val SUBTYPE_NAME_RESOURCE_PREFIX = "string/subtype_"
|
||||||
|
private const val SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = "string/subtype_generic_"
|
||||||
|
private const val SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX = "string/subtype_with_layout_"
|
||||||
|
private const val SUBTYPE_NAME_RESOURCE_NO_LANGUAGE_PREFIX = "string/subtype_no_language_"
|
||||||
|
private const val SUBTYPE_NAME_RESOURCE_IN_ROOT_LOCALE_PREFIX = "string/subtype_in_root_locale_"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue