Merge changes Iaf179d60,I315cf372,I21d3c5cc into nyc-dev
* changes: Use LocaleList for implicitly enabled subtypes. Add a utility method to filter locales. Mechanical refactoring in InputMethodUtilsTest.
This commit is contained in:
@@ -32,6 +32,7 @@ import android.text.TextUtils;
|
||||
import android.text.TextUtils.SimpleStringSplitter;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.LocaleList;
|
||||
import android.util.Pair;
|
||||
import android.util.Printer;
|
||||
import android.util.Slog;
|
||||
@@ -486,18 +487,29 @@ public class InputMethodUtils {
|
||||
return NOT_A_SUBTYPE_ID;
|
||||
}
|
||||
|
||||
private static final LocaleUtils.LocaleExtractor<InputMethodSubtype> sSubtypeToLocale =
|
||||
new LocaleUtils.LocaleExtractor<InputMethodSubtype>() {
|
||||
@Override
|
||||
public Locale get(InputMethodSubtype source) {
|
||||
return source != null ? source.getLocaleObject() : null;
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
public static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
|
||||
Resources res, InputMethodInfo imi) {
|
||||
final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
|
||||
final String systemLocale = res.getConfiguration().locale.toString();
|
||||
final LocaleList systemLocales = res.getConfiguration().getLocales();
|
||||
final String systemLocale = systemLocales.get(0).toString();
|
||||
if (TextUtils.isEmpty(systemLocale)) return new ArrayList<>();
|
||||
final String systemLanguage = res.getConfiguration().locale.getLanguage();
|
||||
final int numSubtypes = subtypes.size();
|
||||
|
||||
// Handle overridesImplicitlyEnabledSubtype mechanism.
|
||||
final String systemLanguage = systemLocales.get(0).getLanguage();
|
||||
final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
|
||||
final int N = subtypes.size();
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (int i = 0; i < numSubtypes; ++i) {
|
||||
// scan overriding implicitly enabled subtypes.
|
||||
InputMethodSubtype subtype = subtypes.get(i);
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
if (subtype.overridesImplicitlyEnabledSubtype()) {
|
||||
final String mode = subtype.getMode();
|
||||
if (!applicableModeAndSubtypesMap.containsKey(mode)) {
|
||||
@@ -508,42 +520,46 @@ public class InputMethodUtils {
|
||||
if (applicableModeAndSubtypesMap.size() > 0) {
|
||||
return new ArrayList<>(applicableModeAndSubtypesMap.values());
|
||||
}
|
||||
for (int i = 0; i < N; ++i) {
|
||||
|
||||
final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
|
||||
for (int i = 0; i < numSubtypes; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
final String locale = subtype.getLocale();
|
||||
final String mode = subtype.getMode();
|
||||
final String language = getLanguageFromLocaleString(locale);
|
||||
// When system locale starts with subtype's locale, that subtype will be applicable
|
||||
// for system locale. We need to make sure the languages are the same, to prevent
|
||||
// locales like "fil" (Filipino) being matched by "fi" (Finnish).
|
||||
//
|
||||
// For instance, it's clearly applicable for cases like system locale = en_US and
|
||||
// subtype = en, but it is not necessarily considered applicable for cases like system
|
||||
// locale = en and subtype = en_US.
|
||||
//
|
||||
// We just call systemLocale.startsWith(locale) in this function because there is no
|
||||
// need to find applicable subtypes aggressively unlike
|
||||
// findLastResortApplicableSubtypeLocked.
|
||||
//
|
||||
// TODO: This check is broken. It won't take scripts into account and doesn't
|
||||
// account for the mandatory conversions performed by Locale#toString.
|
||||
if (language.equals(systemLanguage) && systemLocale.startsWith(locale)) {
|
||||
final InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode);
|
||||
// If more applicable subtypes are contained, skip.
|
||||
if (applicableSubtype != null) {
|
||||
if (systemLocale.equals(applicableSubtype.getLocale())) continue;
|
||||
if (!systemLocale.equals(locale)) continue;
|
||||
if (TextUtils.equals(SUBTYPE_MODE_KEYBOARD, subtype.getMode())) {
|
||||
keyboardSubtypes.add(subtype);
|
||||
} else {
|
||||
final Locale locale = subtype.getLocaleObject();
|
||||
final String mode = subtype.getMode();
|
||||
// TODO: Take secondary system locales into consideration.
|
||||
if (locale != null && locale.equals(systemLanguage)) {
|
||||
final InputMethodSubtype applicableSubtype =
|
||||
applicableModeAndSubtypesMap.get(mode);
|
||||
// If more applicable subtypes are contained, skip.
|
||||
if (applicableSubtype != null) {
|
||||
if (systemLocale.equals(applicableSubtype.getLocaleObject())) continue;
|
||||
if (!systemLocale.equals(locale)) continue;
|
||||
}
|
||||
applicableModeAndSubtypesMap.put(mode, subtype);
|
||||
}
|
||||
applicableModeAndSubtypesMap.put(mode, subtype);
|
||||
}
|
||||
}
|
||||
final InputMethodSubtype keyboardSubtype
|
||||
= applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD);
|
||||
final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<>(
|
||||
applicableModeAndSubtypesMap.values());
|
||||
if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
final InputMethodSubtype subtype = subtypes.get(i);
|
||||
|
||||
final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(keyboardSubtypes, sSubtypeToLocale, systemLocales,
|
||||
applicableSubtypes);
|
||||
|
||||
boolean hasAsciiCapableKeyboard = false;
|
||||
final int numApplicationSubtypes = applicableSubtypes.size();
|
||||
for (int i = 0; i < numApplicationSubtypes; ++i) {
|
||||
final InputMethodSubtype subtype = applicableSubtypes.get(i);
|
||||
if (subtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
|
||||
hasAsciiCapableKeyboard = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasAsciiCapableKeyboard) {
|
||||
final int numKeyboardSubtypes = keyboardSubtypes.size();
|
||||
for (int i = 0; i < numKeyboardSubtypes; ++i) {
|
||||
final InputMethodSubtype subtype = keyboardSubtypes.get(i);
|
||||
final String mode = subtype.getMode();
|
||||
if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey(
|
||||
TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) {
|
||||
@@ -551,13 +567,16 @@ public class InputMethodUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyboardSubtype == null) {
|
||||
|
||||
if (applicableSubtypes.isEmpty()) {
|
||||
InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
|
||||
res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
|
||||
if (lastResortKeyboardSubtype != null) {
|
||||
applicableSubtypes.add(lastResortKeyboardSubtype);
|
||||
}
|
||||
}
|
||||
|
||||
applicableSubtypes.addAll(applicableModeAndSubtypesMap.values());
|
||||
return applicableSubtypes;
|
||||
}
|
||||
|
||||
|
||||
142
core/java/com/android/internal/inputmethod/LocaleUtils.java
Normal file
142
core/java/com/android/internal/inputmethod/LocaleUtils.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.inputmethod;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.LocaleList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class LocaleUtils {
|
||||
|
||||
@VisibleForTesting
|
||||
public interface LocaleExtractor<T> {
|
||||
@Nullable
|
||||
Locale get(@Nullable T source);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getLanguage(@Nullable Locale locale) {
|
||||
if (locale == null) {
|
||||
return null;
|
||||
}
|
||||
return locale.getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given items based on language preferences.
|
||||
*
|
||||
* <p>For each language found in {@code preferredLanguages}, this method tries to copy at most
|
||||
* one best-match item from {@code source} to {@code dest}. For example, if
|
||||
* {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLanguages},
|
||||
* this method tries to copy at most one English locale, at most one Japanese, and at most one
|
||||
* French locale from {@code source} to {@code dest}. Here the best matching English locale
|
||||
* will be searched from {@code source} as follows.
|
||||
* <ol>
|
||||
* <li>The first instance in {@code sources} that exactly matches {@code "en-GB"}</li>
|
||||
* <li>The first instance in {@code sources} that exactly matches {@code "en-AU"}</li>
|
||||
* <li>The first instance in {@code sources} that exactly matches {@code "en-IN"}</li>
|
||||
* <li>The first instance in {@code sources} that partially matches {@code "en"}</li>
|
||||
* </ol>
|
||||
* <p>Then this method iterates the same algorithm for Japanese then French.</p>
|
||||
*
|
||||
* @param sources Source items to be filtered.
|
||||
* @param extractor Type converter from the source items to {@link Locale} object.
|
||||
* @param preferredLanguages Ordered list of locales with which the input items will be
|
||||
* filtered.
|
||||
* @param dest Destination into which the filtered items will be added.
|
||||
* @param <T> Type of the data items.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static <T> void filterByLanguage(
|
||||
@NonNull List<T> sources,
|
||||
@NonNull LocaleExtractor<T> extractor,
|
||||
@NonNull LocaleList preferredLanguages,
|
||||
@NonNull ArrayList<T> dest) {
|
||||
final Locale[] availableLocales = new Locale[sources.size()];
|
||||
for (int i = 0; i < availableLocales.length; ++i) {
|
||||
availableLocales[i] = extractor.get(sources.get(i));
|
||||
}
|
||||
final Locale[] sortedPreferredLanguages = new Locale[preferredLanguages.size()];
|
||||
if (sortedPreferredLanguages.length > 0) {
|
||||
int nextIndex = 0;
|
||||
final int N = preferredLanguages.size();
|
||||
languageLoop:
|
||||
for (int i = 0; i < N; ++i) {
|
||||
final String language = getLanguage(preferredLanguages.get(i));
|
||||
for (int j = 0; j < nextIndex; ++j) {
|
||||
if (TextUtils.equals(getLanguage(sortedPreferredLanguages[j]), language)) {
|
||||
continue languageLoop;
|
||||
}
|
||||
}
|
||||
for (int j = i; j < N; ++j) {
|
||||
final Locale locale = preferredLanguages.get(j);
|
||||
if (TextUtils.equals(language, getLanguage(locale))) {
|
||||
sortedPreferredLanguages[nextIndex] = locale;
|
||||
++nextIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int languageIndex = 0; languageIndex < sortedPreferredLanguages.length;) {
|
||||
// Finding the range.
|
||||
final String language = getLanguage(sortedPreferredLanguages[languageIndex]);
|
||||
int nextLanguageIndex = languageIndex;
|
||||
for (; nextLanguageIndex < sortedPreferredLanguages.length; ++nextLanguageIndex) {
|
||||
final Locale locale = sortedPreferredLanguages[nextLanguageIndex];
|
||||
if (!TextUtils.equals(getLanguage(locale), language)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check exact match
|
||||
boolean found = false;
|
||||
for (int i = languageIndex; !found && i < nextLanguageIndex; ++i) {
|
||||
final Locale locale = sortedPreferredLanguages[i];
|
||||
for (int j = 0; j < availableLocales.length; ++j) {
|
||||
if (!Objects.equals(locale, availableLocales[j])) {
|
||||
continue;
|
||||
}
|
||||
dest.add(sources.get(j));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// No exact match. Use language match.
|
||||
for (int j = 0; j < availableLocales.length; ++j) {
|
||||
if (!TextUtils.equals(language, getLanguage(availableLocales[j]))) {
|
||||
continue;
|
||||
}
|
||||
dest.add(sources.get(j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
languageIndex = nextLanguageIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Parcel;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
@@ -36,6 +37,10 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.isIn;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
private static final boolean IS_AUX = true;
|
||||
private static final boolean IS_DEFAULT = true;
|
||||
@@ -186,6 +191,9 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB",
|
||||
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
|
||||
IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
|
||||
final InputMethodSubtype nonAutoEnIN = createDummyInputMethodSubtype("en_IN",
|
||||
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
|
||||
IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
|
||||
final InputMethodSubtype nonAutoFrCA = createDummyInputMethodSubtype("fr_CA",
|
||||
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
|
||||
IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
|
||||
@@ -233,9 +241,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_EN_US))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_EN_US), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(autoSubtype, result.get(0));
|
||||
}
|
||||
@@ -257,9 +263,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_EN_US))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_EN_US), imi);
|
||||
verifyEquality(nonAutoEnUS, result.get(0));
|
||||
}
|
||||
|
||||
@@ -279,9 +283,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_EN_GB))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_EN_GB), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoEnGB, result.get(0));
|
||||
}
|
||||
@@ -303,9 +305,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_FR))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_FR), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoFrCA, result.get(0));
|
||||
}
|
||||
@@ -323,9 +323,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_FR_CA))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_FR_CA), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoFrCA, result.get(0));
|
||||
}
|
||||
@@ -344,9 +342,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_JA_JP))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_JA_JP), imi);
|
||||
assertEquals(3, result.size());
|
||||
verifyEquality(nonAutoJa, result.get(0));
|
||||
verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1));
|
||||
@@ -364,9 +360,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_FIL_PH))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_FIL_PH), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoFil, result.get(0));
|
||||
}
|
||||
@@ -384,9 +378,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_FI))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_FI), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoJa, result.get(0));
|
||||
}
|
||||
@@ -402,9 +394,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_IN))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_IN), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoIn, result.get(0));
|
||||
}
|
||||
@@ -418,9 +408,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_ID))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_ID), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoIn, result.get(0));
|
||||
}
|
||||
@@ -434,9 +422,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_IN))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_IN), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoId, result.get(0));
|
||||
}
|
||||
@@ -450,12 +436,36 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
createTargetContextWithLocales(new LocaleList(LOCALE_ID))
|
||||
.getResources(),
|
||||
imi);
|
||||
getResourcesForLocales(LOCALE_ID), imi);
|
||||
assertEquals(1, result.size());
|
||||
verifyEquality(nonAutoId, result.get(0));
|
||||
}
|
||||
|
||||
// If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and the system
|
||||
// provides multiple locales, we try to enable multiple subtypes.
|
||||
{
|
||||
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
|
||||
subtypes.add(nonAutoEnUS);
|
||||
subtypes.add(nonAutoFrCA);
|
||||
subtypes.add(nonAutoIn);
|
||||
subtypes.add(nonAutoJa);
|
||||
subtypes.add(nonAutoFil);
|
||||
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
|
||||
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
|
||||
final InputMethodInfo imi = createDummyInputMethodInfo(
|
||||
"com.android.apps.inputmethod.latin",
|
||||
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
|
||||
subtypes);
|
||||
final ArrayList<InputMethodSubtype> result =
|
||||
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
|
||||
getResourcesForLocales(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi);
|
||||
assertThat(nonAutoFrCA, isIn(result));
|
||||
assertThat(nonAutoEnUS, isIn(result));
|
||||
assertThat(nonAutoJa, isIn(result));
|
||||
assertThat(nonAutoIn, not(isIn(result)));
|
||||
assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result)));
|
||||
assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result)));
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@@ -638,6 +648,10 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
|
||||
.createConfigurationContext(resourceConfiguration);
|
||||
}
|
||||
|
||||
private Resources getResourcesForLocales(Locale... locales) {
|
||||
return createTargetContextWithLocales(new LocaleList(locales)).getResources();
|
||||
}
|
||||
|
||||
private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
|
||||
final String[] packageNames = new String[imis.size()];
|
||||
for (int i = 0; i < imis.size(); ++i) {
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.inputmethod;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.LocaleList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LocaleUtilsTest extends InstrumentationTestCase {
|
||||
|
||||
private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper =
|
||||
new LocaleUtils.LocaleExtractor<Locale>() {
|
||||
@Override
|
||||
public Locale get(Locale source) {
|
||||
return source;
|
||||
}
|
||||
};
|
||||
|
||||
@SmallTest
|
||||
public void testFilterByLanguageEmptyLanguageList() throws Exception {
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(Locale.forLanguageTag("en-US"));
|
||||
availableLocales.add(Locale.forLanguageTag("fr-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("in"));
|
||||
availableLocales.add(Locale.forLanguageTag("ja"));
|
||||
availableLocales.add(Locale.forLanguageTag("fil"));
|
||||
|
||||
final LocaleList preferredLocales = LocaleList.getEmptyLocaleList();
|
||||
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(0, dest.size());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testFilterByLanguageEmptySource() throws Exception {
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
|
||||
final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
|
||||
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(0, dest.size());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testFilterByLanguageNullAvailableLocales() throws Exception {
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(null);
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(0, dest.size());
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(null);
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(0, dest.size());
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(Locale.forLanguageTag("en-US"));
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(null);
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(1, dest.size());
|
||||
assertEquals(availableLocales.get(1), dest.get(0)); // "en-US"
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(Locale.forLanguageTag("en"));
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(null);
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(1, dest.size());
|
||||
assertEquals(availableLocales.get(1), dest.get(0)); // "en"
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(Locale.forLanguageTag("ja-JP"));
|
||||
availableLocales.add(null);
|
||||
availableLocales.add(null);
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(0, dest.size());
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testFilterByLanguage() throws Exception {
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(Locale.forLanguageTag("en-US"));
|
||||
availableLocales.add(Locale.forLanguageTag("fr-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("in"));
|
||||
availableLocales.add(Locale.forLanguageTag("ja"));
|
||||
availableLocales.add(Locale.forLanguageTag("fil"));
|
||||
|
||||
final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
|
||||
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(3, dest.size());
|
||||
assertEquals(availableLocales.get(1), dest.get(0)); // "fr-CA"
|
||||
assertEquals(availableLocales.get(0), dest.get(1)); // "en-US"
|
||||
assertEquals(availableLocales.get(3), dest.get(2)); // "ja"
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testFilterByLanguageTheSameLanguage() throws Exception {
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(Locale.forLanguageTag("fr-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("en-US"));
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(1, dest.size());
|
||||
assertEquals(availableLocales.get(1), dest.get(0)); // "en-US"
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(Locale.forLanguageTag("fr-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("en"));
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(1, dest.size());
|
||||
assertEquals(availableLocales.get(1), dest.get(0)); // "en"
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(Locale.forLanguageTag("fr-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("en-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("en-IN"));
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(1, dest.size());
|
||||
assertEquals(availableLocales.get(2), dest.get(0)); // "en-IN"
|
||||
}
|
||||
{
|
||||
final LocaleList preferredLocales =
|
||||
LocaleList.forLanguageTags("en-AU,en-GB,en-US,en-IN");
|
||||
final ArrayList<Locale> availableLocales = new ArrayList<>();
|
||||
availableLocales.add(Locale.forLanguageTag("fr-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("en-CA"));
|
||||
availableLocales.add(Locale.forLanguageTag("en-NZ"));
|
||||
availableLocales.add(Locale.forLanguageTag("en-BZ"));
|
||||
final ArrayList<Locale> dest = new ArrayList<>();
|
||||
LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
|
||||
assertEquals(1, dest.size());
|
||||
assertEquals(availableLocales.get(1), dest.get(0)); // "en-CA"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,7 @@ import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.EventLog;
|
||||
import android.util.LocaleList;
|
||||
import android.util.LruCache;
|
||||
import android.util.Pair;
|
||||
import android.util.PrintWriterPrinter;
|
||||
@@ -135,7 +136,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class provides a system service that manages input methods.
|
||||
@@ -446,7 +446,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
private View mSwitchingDialogTitleView;
|
||||
private InputMethodInfo[] mIms;
|
||||
private int[] mSubtypeIds;
|
||||
private Locale mLastSystemLocale;
|
||||
private LocaleList mLastSystemLocales;
|
||||
private boolean mShowImeWithHardKeyboard;
|
||||
private boolean mAccessibilityRequestingNoSoftKeyboard;
|
||||
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
|
||||
@@ -949,15 +949,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
// not system ready
|
||||
return;
|
||||
}
|
||||
final Locale newLocale = mRes.getConfiguration().locale;
|
||||
final LocaleList newLocales = mRes.getConfiguration().getLocales();
|
||||
if (!updateOnlyWhenLocaleChanged
|
||||
|| (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
|
||||
|| (newLocales != null && !newLocales.equals(mLastSystemLocales))) {
|
||||
if (!updateOnlyWhenLocaleChanged) {
|
||||
hideCurrentInputLocked(0, null);
|
||||
resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_RESET_IME);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Locale has been changed to " + newLocale);
|
||||
Slog.i(TAG, "LocaleList has been changed to " + newLocales);
|
||||
}
|
||||
buildInputMethodListLocked(resetDefaultEnabledIme);
|
||||
if (!updateOnlyWhenLocaleChanged) {
|
||||
@@ -972,7 +972,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
resetDefaultImeLocked(mContext);
|
||||
}
|
||||
updateFromSettingsLocked(true);
|
||||
mLastSystemLocale = newLocale;
|
||||
mLastSystemLocales = newLocales;
|
||||
if (!updateOnlyWhenLocaleChanged) {
|
||||
try {
|
||||
startInputInnerLocked();
|
||||
@@ -1079,7 +1079,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
mSettings.getEnabledInputMethodListLocked(),
|
||||
mSettings.getCurrentUserId(), mContext.getBasePackageName());
|
||||
}
|
||||
mLastSystemLocale = mRes.getConfiguration().locale;
|
||||
mLastSystemLocales = mRes.getConfiguration().getLocales();
|
||||
try {
|
||||
startInputInnerLocked();
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
Reference in New Issue
Block a user