Merge "Check system locale when picking up an initial SpellChecker." into mnc-dev
This commit is contained in:
@@ -1302,4 +1302,131 @@ public class InputMethodUtils {
|
||||
return enabledInputMethodAndSubtypes;
|
||||
}
|
||||
}
|
||||
|
||||
// For spell checker service manager.
|
||||
// TODO: Should we have TextServicesUtils.java?
|
||||
private static final Locale LOCALE_EN_US = new Locale("en", "US");
|
||||
private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Locale} in the order of appropriateness for the default spell
|
||||
* checker service.
|
||||
*
|
||||
* <p>If the system language is English, and the region is also explicitly specified in the
|
||||
* system locale, the following fallback order will be applied.</p>
|
||||
* <ul>
|
||||
* <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li>
|
||||
* <li>(system-locale-language, system-locale-region)</li>
|
||||
* <li>("en", "US")</li>
|
||||
* <li>("en", "GB")</li>
|
||||
* <li>("en")</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>If the system language is English, but no region is specified in the system locale,
|
||||
* the following fallback order will be applied.</p>
|
||||
* <ul>
|
||||
* <li>("en")</li>
|
||||
* <li>("en", "US")</li>
|
||||
* <li>("en", "GB")</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>If the system language is not English, the following fallback order will be applied.</p>
|
||||
* <ul>
|
||||
* <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li>
|
||||
* <li>(system-locale-language, system-locale-region) (if exists)</li>
|
||||
* <li>(system-locale-language) (if exists)</li>
|
||||
* <li>("en", "US")</li>
|
||||
* <li>("en", "GB")</li>
|
||||
* <li>("en")</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param systemLocale the current system locale to be taken into consideration.
|
||||
* @return a list of {@link Locale}. The first one is considered to be most appropriate.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static ArrayList<Locale> getSuitableLocalesForSpellChecker(
|
||||
@Nullable final Locale systemLocale) {
|
||||
final Locale systemLocaleLanguageCountryVariant;
|
||||
final Locale systemLocaleLanguageCountry;
|
||||
final Locale systemLocaleLanguage;
|
||||
if (systemLocale != null) {
|
||||
final String language = systemLocale.getLanguage();
|
||||
final boolean hasLanguage = !TextUtils.isEmpty(language);
|
||||
final String country = systemLocale.getCountry();
|
||||
final boolean hasCountry = !TextUtils.isEmpty(country);
|
||||
final String variant = systemLocale.getVariant();
|
||||
final boolean hasVariant = !TextUtils.isEmpty(variant);
|
||||
if (hasLanguage && hasCountry && hasVariant) {
|
||||
systemLocaleLanguageCountryVariant = new Locale(language, country, variant);
|
||||
} else {
|
||||
systemLocaleLanguageCountryVariant = null;
|
||||
}
|
||||
if (hasLanguage && hasCountry) {
|
||||
systemLocaleLanguageCountry = new Locale(language, country);
|
||||
} else {
|
||||
systemLocaleLanguageCountry = null;
|
||||
}
|
||||
if (hasLanguage) {
|
||||
systemLocaleLanguage = new Locale(language);
|
||||
} else {
|
||||
systemLocaleLanguage = null;
|
||||
}
|
||||
} else {
|
||||
systemLocaleLanguageCountryVariant = null;
|
||||
systemLocaleLanguageCountry = null;
|
||||
systemLocaleLanguage = null;
|
||||
}
|
||||
|
||||
final ArrayList<Locale> locales = new ArrayList<>();
|
||||
if (systemLocaleLanguageCountryVariant != null) {
|
||||
locales.add(systemLocaleLanguageCountryVariant);
|
||||
}
|
||||
|
||||
if (Locale.ENGLISH.equals(systemLocaleLanguage)) {
|
||||
if (systemLocaleLanguageCountry != null) {
|
||||
// If the system language is English, and the region is also explicitly specified,
|
||||
// following fallback order will be applied.
|
||||
// - systemLocaleLanguageCountry [if systemLocaleLanguageCountry is non-null]
|
||||
// - en_US [if systemLocaleLanguageCountry is non-null and not en_US]
|
||||
// - en_GB [if systemLocaleLanguageCountry is non-null and not en_GB]
|
||||
// - en
|
||||
if (systemLocaleLanguageCountry != null) {
|
||||
locales.add(systemLocaleLanguageCountry);
|
||||
}
|
||||
if (!LOCALE_EN_US.equals(systemLocaleLanguageCountry)) {
|
||||
locales.add(LOCALE_EN_US);
|
||||
}
|
||||
if (!LOCALE_EN_GB.equals(systemLocaleLanguageCountry)) {
|
||||
locales.add(LOCALE_EN_GB);
|
||||
}
|
||||
locales.add(Locale.ENGLISH);
|
||||
} else {
|
||||
// If the system language is English, but no region is specified, following
|
||||
// fallback order will be applied.
|
||||
// - en
|
||||
// - en_US
|
||||
// - en_GB
|
||||
locales.add(Locale.ENGLISH);
|
||||
locales.add(LOCALE_EN_US);
|
||||
locales.add(LOCALE_EN_GB);
|
||||
}
|
||||
} else {
|
||||
// If the system language is not English, the fallback order will be
|
||||
// - systemLocaleLanguageCountry [if non-null]
|
||||
// - systemLocaleLanguage [if non-null]
|
||||
// - en_US
|
||||
// - en_GB
|
||||
// - en
|
||||
if (systemLocaleLanguageCountry != null) {
|
||||
locales.add(systemLocaleLanguageCountry);
|
||||
}
|
||||
if (systemLocaleLanguage != null) {
|
||||
locales.add(systemLocaleLanguage);
|
||||
}
|
||||
locales.add(LOCALE_EN_US);
|
||||
locales.add(LOCALE_EN_GB);
|
||||
locales.add(Locale.ENGLISH);
|
||||
}
|
||||
return locales;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,11 +51,15 @@ public class InputMethodTest extends InstrumentationTestCase {
|
||||
private static final Locale LOCALE_FR = new Locale("fr");
|
||||
private static final Locale LOCALE_FR_CA = new Locale("fr", "CA");
|
||||
private static final Locale LOCALE_HI = new Locale("hi");
|
||||
private static final Locale LOCALE_JA = new Locale("ja");
|
||||
private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
|
||||
private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
|
||||
private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW");
|
||||
private static final Locale LOCALE_IN = new Locale("in");
|
||||
private static final Locale LOCALE_ID = new Locale("id");
|
||||
private static final Locale LOCALE_TH = new Locale("ht");
|
||||
private static final Locale LOCALE_TH_TH = new Locale("ht", "TH");
|
||||
private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH");
|
||||
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
|
||||
private static final String SUBTYPE_MODE_VOICE = "voice";
|
||||
private static final String SUBTYPE_MODE_ANY = null;
|
||||
@@ -849,4 +853,99 @@ public class InputMethodTest extends InstrumentationTestCase {
|
||||
|
||||
return preinstalledImes;
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testGetSuitableLocalesForSpellChecker() throws Exception {
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_US);
|
||||
assertEquals(3, locales.size());
|
||||
assertEquals(LOCALE_EN_US, locales.get(0));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(1));
|
||||
assertEquals(LOCALE_EN, locales.get(2));
|
||||
}
|
||||
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_GB);
|
||||
assertEquals(3, locales.size());
|
||||
assertEquals(LOCALE_EN_GB, locales.get(0));
|
||||
assertEquals(LOCALE_EN_US, locales.get(1));
|
||||
assertEquals(LOCALE_EN, locales.get(2));
|
||||
}
|
||||
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN);
|
||||
assertEquals(3, locales.size());
|
||||
assertEquals(LOCALE_EN, locales.get(0));
|
||||
assertEquals(LOCALE_EN_US, locales.get(1));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(2));
|
||||
}
|
||||
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_IN);
|
||||
assertEquals(4, locales.size());
|
||||
assertEquals(LOCALE_EN_IN, locales.get(0));
|
||||
assertEquals(LOCALE_EN_US, locales.get(1));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(2));
|
||||
assertEquals(LOCALE_EN, locales.get(3));
|
||||
}
|
||||
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_JA_JP);
|
||||
assertEquals(5, locales.size());
|
||||
assertEquals(LOCALE_JA_JP, locales.get(0));
|
||||
assertEquals(LOCALE_JA, locales.get(1));
|
||||
assertEquals(LOCALE_EN_US, locales.get(2));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(3));
|
||||
assertEquals(Locale.ENGLISH, locales.get(4));
|
||||
}
|
||||
|
||||
// Test 3-letter language code.
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_FIL_PH);
|
||||
assertEquals(5, locales.size());
|
||||
assertEquals(LOCALE_FIL_PH, locales.get(0));
|
||||
assertEquals(LOCALE_FIL, locales.get(1));
|
||||
assertEquals(LOCALE_EN_US, locales.get(2));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(3));
|
||||
assertEquals(Locale.ENGLISH, locales.get(4));
|
||||
}
|
||||
|
||||
// Test variant.
|
||||
{
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_TH_TH_TH);
|
||||
assertEquals(6, locales.size());
|
||||
assertEquals(LOCALE_TH_TH_TH, locales.get(0));
|
||||
assertEquals(LOCALE_TH_TH, locales.get(1));
|
||||
assertEquals(LOCALE_TH, locales.get(2));
|
||||
assertEquals(LOCALE_EN_US, locales.get(3));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(4));
|
||||
assertEquals(Locale.ENGLISH, locales.get(5));
|
||||
}
|
||||
|
||||
// Test Locale extension.
|
||||
{
|
||||
final Locale localeWithoutVariant = LOCALE_JA_JP;
|
||||
final Locale localeWithVariant = new Locale.Builder()
|
||||
.setLocale(LOCALE_JA_JP)
|
||||
.setExtension('x', "android")
|
||||
.build();
|
||||
assertFalse(localeWithoutVariant.equals(localeWithVariant));
|
||||
|
||||
final ArrayList<Locale> locales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(localeWithVariant);
|
||||
assertEquals(5, locales.size());
|
||||
assertEquals(LOCALE_JA_JP, locales.get(0));
|
||||
assertEquals(LOCALE_JA, locales.get(1));
|
||||
assertEquals(LOCALE_EN_US, locales.get(2));
|
||||
assertEquals(LOCALE_EN_GB, locales.get(3));
|
||||
assertEquals(Locale.ENGLISH, locales.get(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.internal.inputmethod.InputMethodUtils;
|
||||
import com.android.internal.textservice.ISpellCheckerService;
|
||||
import com.android.internal.textservice.ISpellCheckerSession;
|
||||
import com.android.internal.textservice.ISpellCheckerSessionListener;
|
||||
@@ -61,9 +62,11 @@ import android.view.textservice.SpellCheckerSubtype;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@@ -141,7 +144,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings);
|
||||
SpellCheckerInfo sci = getCurrentSpellChecker(null);
|
||||
if (sci == null) {
|
||||
sci = findAvailSpellCheckerLocked(null, null);
|
||||
sci = findAvailSpellCheckerLocked(null);
|
||||
if (sci != null) {
|
||||
// Set the current spell checker if there is one or more spell checkers
|
||||
// available. In this case, "sci" is the first one in the available spell
|
||||
@@ -190,7 +193,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE
|
||||
// Package modified
|
||||
|| isPackageModified(packageName)) {
|
||||
sci = findAvailSpellCheckerLocked(null, packageName);
|
||||
sci = findAvailSpellCheckerLocked(packageName);
|
||||
if (sci != null) {
|
||||
setCurrentSpellCheckerLocked(sci.getId());
|
||||
}
|
||||
@@ -331,8 +334,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
mSpellCheckerBindGroups.clear();
|
||||
}
|
||||
|
||||
// TODO: find an appropriate spell checker for specified locale
|
||||
private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) {
|
||||
private SpellCheckerInfo findAvailSpellCheckerLocked(String prefPackage) {
|
||||
final int spellCheckersCount = mSpellCheckerList.size();
|
||||
if (spellCheckersCount == 0) {
|
||||
Slog.w(TAG, "no available spell checker services found");
|
||||
@@ -349,6 +351,38 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up a spell checker based on the system locale.
|
||||
// TODO: Still there is a room to improve in the following logic: e.g., check if the package
|
||||
// is pre-installed or not.
|
||||
final Locale systemLocal = mContext.getResources().getConfiguration().locale;
|
||||
final ArrayList<Locale> suitableLocales =
|
||||
InputMethodUtils.getSuitableLocalesForSpellChecker(systemLocal);
|
||||
if (DBG) {
|
||||
Slog.w(TAG, "findAvailSpellCheckerLocked suitableLocales="
|
||||
+ Arrays.toString(suitableLocales.toArray(new Locale[suitableLocales.size()])));
|
||||
}
|
||||
final int localeCount = suitableLocales.size();
|
||||
for (int localeIndex = 0; localeIndex < localeCount; ++localeIndex) {
|
||||
final Locale locale = suitableLocales.get(localeIndex);
|
||||
for (int spellCheckersIndex = 0; spellCheckersIndex < spellCheckersCount;
|
||||
++spellCheckersIndex) {
|
||||
final SpellCheckerInfo info = mSpellCheckerList.get(spellCheckersIndex);
|
||||
final int subtypeCount = info.getSubtypeCount();
|
||||
for (int subtypeIndex = 0; subtypeIndex < subtypeCount; ++subtypeIndex) {
|
||||
final SpellCheckerSubtype subtype = info.getSubtypeAt(subtypeIndex);
|
||||
final Locale subtypeLocale = InputMethodUtils.constructLocaleFromString(
|
||||
subtype.getLocale());
|
||||
if (locale.equals(subtypeLocale)) {
|
||||
// TODO: We may have more spell checkers that fall into this category.
|
||||
// Ideally we should pick up the most suitable one instead of simply
|
||||
// returning the first found one.
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spellCheckersCount > 1) {
|
||||
Slog.w(TAG, "more than one spell checker service found, picking first");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user