summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/app/AppLocaleCollector.java258
-rw-r--r--core/java/com/android/internal/app/AppLocaleStore.java14
-rw-r--r--core/java/com/android/internal/app/LocaleStore.java52
-rw-r--r--tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java170
-rw-r--r--tests/Internal/src/com/android/internal/app/LocaleStoreTest.java63
5 files changed, 518 insertions, 39 deletions
diff --git a/core/java/com/android/internal/app/AppLocaleCollector.java b/core/java/com/android/internal/app/AppLocaleCollector.java
index 65e8c646e17d..a50fbb81dc6b 100644
--- a/core/java/com/android/internal/app/AppLocaleCollector.java
+++ b/core/java/com/android/internal/app/AppLocaleCollector.java
@@ -19,49 +19,184 @@ package com.android.internal.app;
import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
+import android.app.LocaleManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.LocaleList;
+import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Set;
+import java.util.stream.Collectors;
/** The Locale data collector for per-app language. */
-class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
+public class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
private static final String TAG = AppLocaleCollector.class.getSimpleName();
private final Context mContext;
private final String mAppPackageName;
- private final LocaleStore.LocaleInfo mAppCurrentLocale;
+ private LocaleStore.LocaleInfo mAppCurrentLocale;
+ private Set<LocaleStore.LocaleInfo> mAllAppActiveLocales;
+ private Set<LocaleStore.LocaleInfo> mImeLocales;
+ private static final String PROP_APP_LANGUAGE_SUGGESTION =
+ "android.app.language.suggestion.enhanced";
+ private static final boolean ENABLED = true;
- AppLocaleCollector(Context context, String appPackageName) {
+ public AppLocaleCollector(Context context, String appPackageName) {
mContext = context;
mAppPackageName = appPackageName;
- mAppCurrentLocale = LocaleStore.getAppCurrentLocaleInfo(
- mContext, mAppPackageName);
+ }
+
+ @VisibleForTesting
+ public LocaleStore.LocaleInfo getAppCurrentLocale() {
+ return LocaleStore.getAppActivatedLocaleInfo(mContext, mAppPackageName, true);
+ }
+
+ /**
+ * Get all applications' activated locales.
+ * @return A set which includes all applications' activated LocaleInfo.
+ */
+ @VisibleForTesting
+ public Set<LocaleStore.LocaleInfo> getAllAppActiveLocales() {
+ PackageManager pm = mContext.getPackageManager();
+ LocaleManager lm = mContext.getSystemService(LocaleManager.class);
+ HashSet<LocaleStore.LocaleInfo> result = new HashSet<>();
+ if (pm != null && lm != null) {
+ HashMap<String, LocaleStore.LocaleInfo> map = new HashMap<>();
+ for (ApplicationInfo appInfo : pm.getInstalledApplications(
+ PackageManager.ApplicationInfoFlags.of(0))) {
+ LocaleStore.LocaleInfo localeInfo = LocaleStore.getAppActivatedLocaleInfo(
+ mContext, appInfo.packageName, false);
+ // For the locale to be added into the suggestion area, its country could not be
+ // empty.
+ if (localeInfo != null && localeInfo.getLocale().getCountry().length() > 0) {
+ map.put(localeInfo.getId(), localeInfo);
+ }
+ }
+ map.forEach((language, localeInfo) -> result.add(localeInfo));
+ }
+ return result;
+ }
+
+ /**
+ * Get all locales that active IME supports.
+ *
+ * @return A set which includes all LocaleInfo that active IME supports.
+ */
+ @VisibleForTesting
+ public Set<LocaleStore.LocaleInfo> getActiveImeLocales() {
+ Set<LocaleStore.LocaleInfo> activeImeLocales = null;
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ if (imm != null) {
+ InputMethodInfo activeIme = getActiveIme(imm);
+ if (activeIme != null) {
+ activeImeLocales = LocaleStore.transformImeLanguageTagToLocaleInfo(
+ imm.getEnabledInputMethodSubtypeList(activeIme, true));
+ }
+ }
+ if (activeImeLocales == null) {
+ return Set.of();
+ } else {
+ return activeImeLocales.stream().filter(
+ // For the locale to be added into the suggestion area, its country could not be
+ // empty.
+ info -> info.getLocale().getCountry().length() > 0).collect(
+ Collectors.toSet());
+ }
+ }
+
+ private InputMethodInfo getActiveIme(InputMethodManager imm) {
+ InputMethodInfo activeIme = null;
+ List<InputMethodInfo> infoList = imm.getEnabledInputMethodList();
+ String imeId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, mContext.getUserId());
+ for (InputMethodInfo method : infoList) {
+ if (method.getId().equals(imeId)) {
+ activeIme = method;
+ }
+ }
+ return activeIme;
+ }
+
+ /**
+ * Get the AppLocaleResult that the application supports.
+ * @return The AppLocaleResult that the application supports.
+ */
+ @VisibleForTesting
+ public AppLocaleStore.AppLocaleResult getAppSupportedLocales() {
+ return AppLocaleStore.getAppSupportedLocales(mContext, mAppPackageName);
+ }
+
+ /**
+ * Get the locales that system supports excluding langTagsToIgnore.
+ *
+ * @param langTagsToIgnore A language set to be ignored.
+ * @param parent The parent locale.
+ * @param translatedOnly specified if is is only for translation.
+ * @return A set which includes the LocaleInfo that system supports, excluding langTagsToIgnore.
+ */
+ @VisibleForTesting
+ public Set<LocaleStore.LocaleInfo> getSystemSupportedLocale(Set<String> langTagsToIgnore,
+ LocaleStore.LocaleInfo parent, boolean translatedOnly) {
+ return LocaleStore.getLevelLocales(mContext, langTagsToIgnore, parent, translatedOnly);
+ }
+
+ /**
+ * Get the locales that system activates.
+ * @return A set which includes all the locales that system activates.
+ */
+ @VisibleForTesting
+ public List<LocaleStore.LocaleInfo> getSystemCurrentLocale() {
+ return LocaleStore.getSystemCurrentLocaleInfo();
}
@Override
public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
HashSet<String> langTagsToIgnore = new HashSet<>();
- LocaleList systemLangList = LocaleList.getDefault();
- for(int i = 0; i < systemLangList.size(); i++) {
- langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
- }
-
if (mAppCurrentLocale != null) {
langTagsToIgnore.add(mAppCurrentLocale.getLocale().toLanguageTag());
}
+
+ if (SystemProperties.getBoolean(PROP_APP_LANGUAGE_SUGGESTION, ENABLED)) {
+ // Add the locale that other App activated
+ mAllAppActiveLocales.forEach(
+ info -> langTagsToIgnore.add(info.getLocale().toLanguageTag()));
+ // Add the locale that active IME enabled
+ mImeLocales.forEach(info -> langTagsToIgnore.add(info.getLocale().toLanguageTag()));
+ }
+
+ // Add System locales
+ LocaleList systemLangList = LocaleList.getDefault();
+ for (int i = 0; i < systemLangList.size(); i++) {
+ langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
+ }
return langTagsToIgnore;
}
@Override
public Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
boolean translatedOnly, boolean isForCountryMode) {
- AppLocaleStore.AppLocaleResult result =
- AppLocaleStore.getAppSupportedLocales(mContext, mAppPackageName);
+ if (mAppCurrentLocale == null) {
+ mAppCurrentLocale = getAppCurrentLocale();
+ }
+ if (mAllAppActiveLocales == null) {
+ mAllAppActiveLocales = getAllAppActiveLocales();
+ }
+ if (mImeLocales == null) {
+ mImeLocales = getActiveImeLocales();
+ }
+ AppLocaleStore.AppLocaleResult result = getAppSupportedLocales();
Set<String> langTagsToIgnore = getIgnoredLocaleList(translatedOnly);
Set<LocaleStore.LocaleInfo> appLocaleList = new HashSet<>();
Set<LocaleStore.LocaleInfo> systemLocaleList;
@@ -71,11 +206,9 @@ class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
// Get system supported locale list
if (isForCountryMode) {
- systemLocaleList = LocaleStore.getLevelLocales(mContext,
- langTagsToIgnore, parent, translatedOnly);
+ systemLocaleList = getSystemSupportedLocale(langTagsToIgnore, parent, translatedOnly);
} else {
- systemLocaleList = LocaleStore.getLevelLocales(mContext, langTagsToIgnore,
- null /* no parent */, translatedOnly);
+ systemLocaleList = getSystemSupportedLocale(langTagsToIgnore, null, translatedOnly);
}
// Add current app locale
@@ -84,19 +217,46 @@ class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
}
// Add current system language into suggestion list
- for(LocaleStore.LocaleInfo localeInfo:
- LocaleStore.getSystemCurrentLocaleInfo()) {
- boolean isNotCurrentLocale = mAppCurrentLocale == null
- || !localeInfo.getLocale().equals(mAppCurrentLocale.getLocale());
- if (!isForCountryMode && isNotCurrentLocale) {
- appLocaleList.add(localeInfo);
+ if (!isForCountryMode) {
+ boolean isCurrentLocale, isInAppOrIme;
+ for (LocaleStore.LocaleInfo localeInfo : getSystemCurrentLocale()) {
+ isCurrentLocale = mAppCurrentLocale != null
+ && localeInfo.getLocale().equals(mAppCurrentLocale.getLocale());
+ isInAppOrIme = existsInAppOrIme(localeInfo.getLocale());
+ if (!isCurrentLocale && !isInAppOrIme) {
+ appLocaleList.add(localeInfo);
+ }
}
}
- // Add the languages that included in system supported locale
+ // Add the languages that are included in system supported locale
+ Set<LocaleStore.LocaleInfo> suggestedSet = null;
if (shouldShowList) {
- appLocaleList.addAll(filterTheLanguagesNotIncludedInSystemLocale(
- systemLocaleList, result.mAppSupportedLocales));
+ appLocaleList.addAll(filterSupportedLocales(systemLocaleList,
+ result.mAppSupportedLocales));
+ suggestedSet = getSuggestedLocales(appLocaleList);
+ }
+
+ if (!isForCountryMode && SystemProperties.getBoolean(PROP_APP_LANGUAGE_SUGGESTION,
+ ENABLED)) {
+ // Add the language that other apps activate into the suggestion list.
+ Set<LocaleStore.LocaleInfo> localeSet = filterSupportedLocales(mAllAppActiveLocales,
+ result.mAppSupportedLocales);
+ if (suggestedSet != null) {
+ // Filter out the locale with the same language and country
+ // like zh-TW vs zh-Hant-TW.
+ localeSet = filterSameLanguageAndCountry(localeSet, suggestedSet);
+ }
+ appLocaleList.addAll(localeSet);
+ suggestedSet.addAll(localeSet);
+
+ // Add the language that the active IME enables into the suggestion list.
+ localeSet = filterSupportedLocales(mImeLocales, result.mAppSupportedLocales);
+ if (suggestedSet != null) {
+ localeSet = filterSameLanguageAndCountry(localeSet, suggestedSet);
+ }
+ appLocaleList.addAll(localeSet);
+ suggestedSet.addAll(localeSet);
}
// Add "system language" option
@@ -117,17 +277,55 @@ class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
return true;
}
- private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotIncludedInSystemLocale(
- Set<LocaleStore.LocaleInfo> systemLocaleList,
+ private Set<LocaleStore.LocaleInfo> getSuggestedLocales(Set<LocaleStore.LocaleInfo> localeSet) {
+ return localeSet.stream().filter(localeInfo -> localeInfo.isSuggested()).collect(
+ Collectors.toSet());
+ }
+
+ private Set<LocaleStore.LocaleInfo> filterSameLanguageAndCountry(
+ Set<LocaleStore.LocaleInfo> newLocaleList,
+ Set<LocaleStore.LocaleInfo> existingLocaleList) {
+ Set<LocaleStore.LocaleInfo> result = new HashSet<>(newLocaleList.size());
+ for (LocaleStore.LocaleInfo appLocaleInfo : newLocaleList) {
+ boolean same = false;
+ Locale appLocale = appLocaleInfo.getLocale();
+ for (LocaleStore.LocaleInfo localeInfo : existingLocaleList) {
+ Locale suggested = localeInfo.getLocale();
+ if (appLocale.getLanguage().equals(suggested.getLanguage())
+ && appLocale.getCountry().equals(suggested.getCountry())) {
+ same = true;
+ break;
+ }
+ }
+ if (!same) {
+ result.add(appLocaleInfo);
+ }
+ }
+ return result;
+ }
+
+ private boolean existsInAppOrIme(Locale locale) {
+ boolean existInApp = mAllAppActiveLocales.stream().anyMatch(
+ localeInfo -> localeInfo.getLocale().equals(locale));
+ if (existInApp) {
+ return true;
+ } else {
+ return mImeLocales.stream().anyMatch(
+ localeInfo -> localeInfo.getLocale().equals(locale));
+ }
+ }
+
+ private Set<LocaleStore.LocaleInfo> filterSupportedLocales(
+ Set<LocaleStore.LocaleInfo> suggestedLocales,
HashSet<Locale> appSupportedLocales) {
Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();
- for(LocaleStore.LocaleInfo li: systemLocaleList) {
+ for (LocaleStore.LocaleInfo li : suggestedLocales) {
if (appSupportedLocales.contains(li.getLocale())) {
filteredList.add(li);
} else {
- for(Locale l: appSupportedLocales) {
- if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
+ for (Locale l : appSupportedLocales) {
+ if (LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
filteredList.add(li);
break;
}
diff --git a/core/java/com/android/internal/app/AppLocaleStore.java b/core/java/com/android/internal/app/AppLocaleStore.java
index f3a322cac79a..a450a05dd315 100644
--- a/core/java/com/android/internal/app/AppLocaleStore.java
+++ b/core/java/com/android/internal/app/AppLocaleStore.java
@@ -30,7 +30,10 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.stream.Collectors;
-class AppLocaleStore {
+/**
+ * A class used to access an application's supporting locales.
+ */
+public class AppLocaleStore {
private static final String TAG = AppLocaleStore.class.getSimpleName();
public static AppLocaleResult getAppSupportedLocales(
@@ -148,8 +151,11 @@ class AppLocaleStore {
return false;
}
- static class AppLocaleResult {
- enum LocaleStatus {
+ /**
+ * A class used to store an application's supporting locales.
+ */
+ public static class AppLocaleResult {
+ public enum LocaleStatus {
UNKNOWN_FAILURE,
NO_SUPPORTED_LANGUAGE_IN_APP,
ASSET_LOCALE_IS_EMPTY,
@@ -158,7 +164,7 @@ class AppLocaleStore {
}
LocaleStatus mLocaleStatus;
- HashSet<Locale> mAppSupportedLocales;
+ public HashSet<Locale> mAppSupportedLocales;
public AppLocaleResult(LocaleStatus localeStatus, HashSet<Locale> appSupportedLocales) {
this.mLocaleStatus = localeStatus;
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 689dec479c89..d2eee91d257f 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -23,6 +23,7 @@ import android.os.LocaleList;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.VisibleForTesting;
@@ -45,10 +46,11 @@ public class LocaleStore {
@VisibleForTesting static final int SUGGESTION_TYPE_SIM = 1 << 0;
@VisibleForTesting static final int SUGGESTION_TYPE_CFG = 1 << 1;
// Only for per-app language picker
- private static final int SUGGESTION_TYPE_CURRENT = 1 << 2;
+ @VisibleForTesting static final int SUGGESTION_TYPE_CURRENT = 1 << 2;
// Only for per-app language picker
- private static final int SUGGESTION_TYPE_SYSTEM_LANGUAGE = 1 << 3;
-
+ @VisibleForTesting static final int SUGGESTION_TYPE_SYSTEM_LANGUAGE = 1 << 3;
+ // Only for per-app language picker
+ @VisibleForTesting static final int SUGGESTION_TYPE_OTHER_APP_LANGUAGE = 1 << 4;
private final Locale mLocale;
private final Locale mParent;
private final String mId;
@@ -259,7 +261,16 @@ public class LocaleStore {
}
}
- public static LocaleInfo getAppCurrentLocaleInfo(Context context, String appPackageName) {
+ /**
+ * Get the application's activated locale.
+ *
+ * @param context UI activity's context.
+ * @param appPackageName The application's package name.
+ * @param isAppSelected True if the application is selected in the UI; false otherwise.
+ * @return A LocaleInfo with the application's activated locale.
+ */
+ public static LocaleInfo getAppActivatedLocaleInfo(Context context, String appPackageName,
+ boolean isAppSelected) {
if (appPackageName == null) {
return null;
}
@@ -272,7 +283,11 @@ public class LocaleStore {
if (locale != null) {
LocaleInfo localeInfo = new LocaleInfo(locale);
- localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT;
+ if (isAppSelected) {
+ localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT;
+ } else {
+ localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE;
+ }
localeInfo.mIsTranslated = true;
return localeInfo;
}
@@ -283,6 +298,24 @@ public class LocaleStore {
}
/**
+ * Transform IME's language tag to LocaleInfo.
+ *
+ * @param list A list which includes IME's subtype.
+ * @return A LocaleInfo set which includes IME's language tags.
+ */
+ public static Set<LocaleInfo> transformImeLanguageTagToLocaleInfo(
+ List<InputMethodSubtype> list) {
+ Set<LocaleInfo> imeLocales = new HashSet<>();
+ for (InputMethodSubtype subtype : list) {
+ LocaleInfo localeInfo = new LocaleInfo(subtype.getLanguageTag());
+ localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE;
+ localeInfo.mIsTranslated = true;
+ imeLocales.add(localeInfo);
+ }
+ return imeLocales;
+ }
+
+ /**
* Returns a list of system languages with LocaleInfo
*/
public static List<LocaleInfo> getSystemCurrentLocaleInfo() {
@@ -458,4 +491,13 @@ public class LocaleStore {
}
return result;
}
+
+ /**
+ * API for testing.
+ */
+ @UnsupportedAppUsage
+ @VisibleForTesting
+ public static LocaleInfo fromLocale(Locale locale) {
+ return new LocaleInfo(locale);
+ }
}
diff --git a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
new file mode 100644
index 000000000000..b24ac3cae795
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2022 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.app;
+
+import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.LocaleStore.LocaleInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Unit tests for the {@link AppLocaleCollector}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppLocaleCollectorTest {
+ private static final String TAG = "AppLocaleCollectorTest";
+ private AppLocaleCollector mAppLocaleCollector;
+ private LocaleStore.LocaleInfo mAppCurrentLocale;
+ private Set<LocaleInfo> mAllAppActiveLocales;
+ private Set<LocaleInfo> mImeLocales;
+ private List<LocaleInfo> mSystemCurrentLocales;
+ private Set<LocaleInfo> mSystemSupportedLocales;
+ private AppLocaleStore.AppLocaleResult mResult;
+ private static final String PKG1 = "pkg1";
+ private static final int NONE = LocaleInfo.SUGGESTION_TYPE_NONE;
+ private static final int SIM = LocaleInfo.SUGGESTION_TYPE_SIM;
+ private static final int CFG = LocaleInfo.SUGGESTION_TYPE_CFG;
+ private static final int SIM_CFG = SIM | CFG;
+ private static final int CURRENT = LocaleInfo.SUGGESTION_TYPE_CURRENT;
+ private static final int SYSTEM = LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE;
+ private static final int OTHERAPP = LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE;
+
+ @Before
+ public void setUp() throws Exception {
+ mAppLocaleCollector = spy(
+ new AppLocaleCollector(InstrumentationRegistry.getContext(), PKG1));
+
+ mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
+ mAllAppActiveLocales = initAllAppActivatedLocales();
+ mImeLocales = initImeLocales();
+ mSystemSupportedLocales = initSystemSupportedLocales();
+ mSystemCurrentLocales = initSystemCurrentLocales();
+ mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
+ initAppSupportedLocale());
+ }
+
+ @Test
+ public void testGetSupportedLocaleList() {
+ doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
+ doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
+ doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
+ doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
+ doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
+ anyObject(), eq(null), eq(true));
+ doReturn(mSystemCurrentLocales).when(mAppLocaleCollector).getSystemCurrentLocale();
+
+ Set<LocaleInfo> result = mAppLocaleCollector.getSupportedLocaleList(null, true, false);
+
+ HashMap<String, Integer> expectedResult = getExpectedResult();
+ assertEquals(result.size(), expectedResult.size());
+ for (LocaleInfo source : result) {
+ int suggestionFlags = expectedResult.getOrDefault(source.getId(), -1);
+ assertEquals(source.mSuggestionFlags, suggestionFlags);
+ }
+ }
+
+ private HashMap<String, Integer> getExpectedResult() {
+ HashMap<String, Integer> map = new HashMap<>();
+ map.put("en-US", CURRENT); // The locale current App activates.
+ map.put("fr", NONE); // The locale App and system support.
+ map.put("zu", NONE); // The locale App and system support.
+ map.put("en", NONE); // Use en because System supports en while APP supports en-CA, en-GB.
+ map.put("ko", NONE); // The locale App and system support.
+ map.put("en-AU", OTHERAPP); // The locale other App activates and current App supports.
+ map.put("en-CA", OTHERAPP); // The locale other App activates and current App supports.
+ map.put("ja-JP", OTHERAPP); // The locale other App activates and current App supports.
+ map.put("zh-Hant-TW", SIM); // The locale system activates.
+ map.put(createLocaleInfo("", SYSTEM).getId(), SYSTEM); // System language title
+ return map;
+ }
+
+ private Set<LocaleInfo> initSystemSupportedLocales() {
+ return Set.of(
+ createLocaleInfo("en", NONE),
+ createLocaleInfo("fr", NONE),
+ createLocaleInfo("zu", NONE),
+ createLocaleInfo("ko", NONE),
+ // will be filtered because current App doesn't support.
+ createLocaleInfo("es-US", SIM_CFG)
+ );
+ }
+
+ private List<LocaleInfo> initSystemCurrentLocales() {
+ return List.of(createLocaleInfo("zh-Hant-TW", SIM),
+ // will be filtered because current App activates this locale.
+ createLocaleInfo("en-US", SIM));
+ }
+
+ private Set<LocaleInfo> initAllAppActivatedLocales() {
+ return Set.of(
+ createLocaleInfo("en-CA", OTHERAPP),
+ createLocaleInfo("en-AU", OTHERAPP),
+ createLocaleInfo("ja-JP", OTHERAPP),
+ // will be filtered because current App activates this locale.
+ createLocaleInfo("en-US", OTHERAPP));
+ }
+
+ private Set<LocaleInfo> initImeLocales() {
+ return Set.of(
+ // will be filtered because system activates zh-Hant-TW.
+ createLocaleInfo("zh-TW", OTHERAPP),
+ // will be filtered because current App's activats this locale.
+ createLocaleInfo("en-US", OTHERAPP));
+ }
+
+ private HashSet<Locale> initAppSupportedLocale() {
+ HashSet<Locale> hs = new HashSet();
+ hs.add(Locale.forLanguageTag("en-US"));
+ hs.add(Locale.forLanguageTag("en-CA"));
+ hs.add(Locale.forLanguageTag("en-GB"));
+ hs.add(Locale.forLanguageTag("zh-TW"));
+ hs.add(Locale.forLanguageTag("ja"));
+ hs.add(Locale.forLanguageTag("fr"));
+ hs.add(Locale.forLanguageTag("zu"));
+ hs.add(Locale.forLanguageTag("ko"));
+ // will be filtered because it's not in the system language.
+ hs.add(Locale.forLanguageTag("mn"));
+ return hs;
+ }
+
+ private LocaleInfo createLocaleInfo(String languageTag, int suggestionFlag) {
+ LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag(languageTag));
+ localeInfo.mSuggestionFlags = suggestionFlag;
+ localeInfo.setTranslated(true);
+ return localeInfo;
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/app/LocaleStoreTest.java b/tests/Internal/src/com/android/internal/app/LocaleStoreTest.java
new file mode 100644
index 000000000000..bf6ece1ac19a
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/app/LocaleStoreTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.LocaleStore.LocaleInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Unit tests for the {@link LocaleStore}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocaleStoreTest {
+ @Before
+ public void setUp() {
+ }
+
+ @Test
+ public void testTransformImeLanguageTagToLocaleInfo() {
+ List<InputMethodSubtype> list = List.of(
+ new InputMethodSubtypeBuilder().setLanguageTag("en-US").build(),
+ new InputMethodSubtypeBuilder().setLanguageTag("zh-TW").build(),
+ new InputMethodSubtypeBuilder().setLanguageTag("ja-JP").build());
+
+ Set<LocaleInfo> localeSet = LocaleStore.transformImeLanguageTagToLocaleInfo(list);
+
+ Set<String> expectedLanguageTag = Set.of("en-US", "zh-TW", "ja-JP");
+ assertEquals(localeSet.size(), expectedLanguageTag.size());
+ for (LocaleInfo info : localeSet) {
+ assertEquals(info.mSuggestionFlags, LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE);
+ assertTrue(expectedLanguageTag.contains(info.getId()));
+ }
+ }
+}