Loading core/java/com/android/internal/app/AppLocaleStore.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * 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 android.app.LocaleConfig; import android.content.Context; import android.content.pm.PackageManager; import android.os.LocaleList; import android.util.Log; import java.util.ArrayList; import java.util.Locale; public class AppLocaleStore { private static final String TAG = AppLocaleStore.class.getSimpleName(); public static ArrayList<Locale> getAppSupportedLocales(Context context, String packageName) { ArrayList<Locale> appSupportedLocales = new ArrayList<>(); LocaleList packageLocaleList = getPackageLocales(context, packageName); if (packageLocaleList != null && packageLocaleList.size() > 0) { for (int i = 0; i < packageLocaleList.size(); i++) { appSupportedLocales.add(packageLocaleList.get(i)); } Log.d(TAG, "getAppSupportedLocales from LocaleConfig. Size: " + appSupportedLocales.size()); } else { String[] languages = getAssetLocales(context, packageName); for (String language : languages) { appSupportedLocales.add(Locale.forLanguageTag(language)); } Log.d(TAG, "getAppSupportedLocales from asset. Size: " + appSupportedLocales.size()); } return appSupportedLocales; } private static LocaleList getPackageLocales(Context context, String packageName) { try { LocaleConfig localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0)); if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { return localeConfig.getSupportedLocales(); } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } return null; } private static String[] getAssetLocales(Context context, String packageName) { try { PackageManager packageManager = context.getPackageManager(); String[] locales = packageManager.getResourcesForApplication( packageManager.getPackageInfo(packageName, PackageManager.MATCH_ALL) .applicationInfo).getAssets().getNonSystemLocales(); if (locales == null) { Log.i(TAG, "[" + packageName + "] locales are null."); return new String[0]; } else if (locales.length <= 0) { Log.i(TAG, "[" + packageName + "] locales length is 0."); return new String[0]; } return locales; } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } return new String[0]; } } core/java/com/android/internal/app/LocaleHelper.java +5 −1 Original line number Diff line number Diff line Loading @@ -234,7 +234,11 @@ public class LocaleHelper { public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) { // We don't care about the various suggestion types, just "suggested" (!= 0) // and "all others" (== 0) if (lhs.isSuggested() == rhs.isSuggested()) { if (lhs.isAppCurrentLocale() || rhs.isAppCurrentLocale()) { return lhs.isAppCurrentLocale() ? -1 : 1; } else if (lhs.isSystemLocale() || rhs.isSystemLocale()) { return lhs.isSystemLocale() ? -1 : 1; } else if (lhs.isSuggested() == rhs.isSuggested()) { // They are in the same "bucket" (suggested / others), so we compare the text return mCollator.compare( removePrefixForCompare(lhs.getLocale(), lhs.getLabel(mCountryMode)), Loading core/java/com/android/internal/app/LocalePickerWithRegion.java +68 −10 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.os.Bundle; import android.os.LocaleList; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; Loading @@ -32,6 +33,7 @@ import android.widget.SearchView; import com.android.internal.R; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Locale; Loading @@ -45,6 +47,7 @@ import java.util.Set; * default locale.</p> */ public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener { private static final String TAG = LocalePickerWithRegion.class.getSimpleName(); private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; private SuggestedLocaleAdapter mAdapter; Loading @@ -57,6 +60,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O private boolean mPreviousSearchHadFocus = false; private int mFirstVisiblePosition = 0; private int mTopDistance = 0; private String mAppPackageName; /** * Other classes can register to be notified when a locale was selected. Loading @@ -73,17 +77,25 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O private static LocalePickerWithRegion createCountryPicker(Context context, LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, boolean translatedOnly) { boolean translatedOnly, String appPackageName) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); boolean shouldShowTheList = localePicker.setListener(context, listener, parent, translatedOnly); translatedOnly, appPackageName); return shouldShowTheList ? localePicker : null; } public static LocalePickerWithRegion createLanguagePicker(Context context, LocaleSelectedListener listener, boolean translatedOnly) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); localePicker.setListener(context, listener, /* parent */ null, translatedOnly); localePicker.setListener(context, listener, /* parent */ null, translatedOnly, null); return localePicker; } public static LocalePickerWithRegion createLanguagePicker(Context context, LocaleSelectedListener listener, boolean translatedOnly, String appPackageName) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); localePicker.setListener( context, listener, /* parent */ null, translatedOnly, appPackageName); return localePicker; } Loading @@ -101,20 +113,32 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O * "pretending" it was selected, and return false.</p> */ private boolean setListener(Context context, LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, boolean translatedOnly) { LocaleStore.LocaleInfo parent, boolean translatedOnly, String appPackageName) { this.mParentLocale = parent; this.mListener = listener; this.mTranslatedOnly = translatedOnly; this.mAppPackageName = appPackageName; setRetainInstance(true); final HashSet<String> langTagsToIgnore = new HashSet<>(); if (!translatedOnly) { LocaleStore.LocaleInfo appCurrentLocale = LocaleStore.getAppCurrentLocaleInfo(context, appPackageName); boolean isForCountryMode = parent != null; if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) { if (appCurrentLocale != null) { Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag()); langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag()); } else { Log.d(TAG, "appCurrentLocale is null"); } } else if (!translatedOnly) { final LocaleList userLocales = LocalePicker.getLocales(); final String[] langTags = userLocales.toLanguageTags().split(","); Collections.addAll(langTagsToIgnore, langTags); } if (parent != null) { if (isForCountryMode) { mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, parent, translatedOnly); if (mLocaleList.size() <= 1) { Loading @@ -127,10 +151,39 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, null /* no parent */, translatedOnly); } Log.d(TAG, "mLocaleList size: " + mLocaleList.size()); // Adding current locale and system default option into suggestion list if(!TextUtils.isEmpty(appPackageName)) { if (appCurrentLocale != null && !isForCountryMode) { mLocaleList.add(appCurrentLocale); } filterTheLanguagesNotSupportedInApp(context, appPackageName); if (!isForCountryMode) { mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo()); } } return true; } private void filterTheLanguagesNotSupportedInApp(Context context, String appPackageName) { ArrayList<Locale> supportedLocales = AppLocaleStore.getAppSupportedLocales(context, appPackageName); Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>(); for(LocaleStore.LocaleInfo li: mLocaleList) { for(Locale l: supportedLocales) { if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) { filteredList.add(li); } } } Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); mLocaleList = filteredList; } private void returnToParentFrame() { getFragmentManager().popBackStack(PARENT_FRAGMENT_NAME, FragmentManager.POP_BACK_STACK_INCLUSIVE); Loading @@ -151,7 +204,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O final boolean countryMode = mParentLocale != null; final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault(); mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode); mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, mAppPackageName); final LocaleHelper.LocaleInfoComparator comp = new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode); mAdapter.sort(comp); Loading Loading @@ -212,17 +265,22 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O @Override public void onListItemClick(ListView l, View v, int position, long id) { SuggestedLocaleAdapter adapter = (SuggestedLocaleAdapter) getListAdapter(); final LocaleStore.LocaleInfo locale = (LocaleStore.LocaleInfo) getListAdapter().getItem(position); (LocaleStore.LocaleInfo) adapter.getItem(position); // Special case for resetting the app locale to equal the system locale. boolean isSystemLocale = locale.isSystemLocale(); boolean isRegionLocale = locale.getParent() != null; if (locale.getParent() != null) { if (isSystemLocale || isRegionLocale) { if (mListener != null) { mListener.onLocaleSelected(locale); } returnToParentFrame(); } else { LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker( getContext(), mListener, locale, mTranslatedOnly /* translate only */); getContext(), mListener, locale, mTranslatedOnly /* translate only */, adapter.getAppPackageName()); if (selector != null) { getFragmentManager().beginTransaction() .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) Loading core/java/com/android/internal/app/LocaleStore.java +49 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,13 @@ package com.android.internal.app; import android.app.LocaleManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.LocaleList; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; import java.io.Serializable; import java.util.HashMap; Loading @@ -31,12 +33,17 @@ import java.util.Set; public class LocaleStore { private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>(); private static final String TAG = LocaleStore.class.getSimpleName(); private static boolean sFullyInitialized = false; public static class LocaleInfo implements Serializable { private static final int SUGGESTION_TYPE_NONE = 0; private static final int SUGGESTION_TYPE_SIM = 1 << 0; private static final int SUGGESTION_TYPE_CFG = 1 << 1; // Only for per-app language picker private static final int SUGGESTION_TYPE_CURRENT = 1 << 2; // Only for per-app language picker private static final int SUGGESTION_TYPE_SYSTEM_LANGUAGE = 1 << 3; private final Locale mLocale; private final Locale mParent; Loading Loading @@ -189,6 +196,14 @@ public class LocaleStore { public void setChecked(boolean checked) { mIsChecked = checked; } public boolean isAppCurrentLocale() { return (mSuggestionFlags & SUGGESTION_TYPE_CURRENT) > 0; } public boolean isSystemLocale() { return (mSuggestionFlags & SUGGESTION_TYPE_SYSTEM_LANGUAGE) > 0; } } private static Set<String> getSimCountries(Context context) { Loading Loading @@ -239,6 +254,40 @@ public class LocaleStore { } } public static LocaleInfo getAppCurrentLocaleInfo(Context context, String appPackageName) { if (appPackageName == null) { return null; } LocaleManager localeManager = context.getSystemService(LocaleManager.class); try { LocaleList localeList = (localeManager == null) ? null : localeManager.getApplicationLocales(appPackageName); Locale locale = localeList == null ? null : localeList.get(0); if (locale != null) { LocaleInfo localeInfo = new LocaleInfo(locale); localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT; localeInfo.mIsTranslated = true; return localeInfo; } } catch (IllegalArgumentException e) { Log.d(TAG, "IllegalArgumentException ", e); } return null; } /** * The "system default" is special case for per-app picker. Intentionally keep the locale * empty to let activity know "system default" been selected. */ public static LocaleInfo getSystemDefaultLocaleInfo() { LocaleInfo systemDefaultInfo = new LocaleInfo(""); systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE; systemDefaultInfo.mIsTranslated = true; return systemDefaultInfo; } /* * Show all the languages supported for a country in the suggested list. * This is also handy for devices without SIM (tablets). Loading core/java/com/android/internal/app/SuggestedLocaleAdapter.java +38 −2 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private static final int TYPE_HEADER_SUGGESTED = 0; private static final int TYPE_HEADER_ALL_OTHERS = 1; private static final int TYPE_LOCALE = 2; private static final int TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER = 3; private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6; private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions; Loading @@ -64,10 +65,18 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private Locale mDisplayLocale = null; // used to potentially cache a modified Context that uses mDisplayLocale private Context mContextOverride = null; private String mAppPackageName; public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) { this(localeOptions, countryMode, null); } public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode, String appPackageName) { mCountryMode = countryMode; mLocaleOptions = new ArrayList<>(localeOptions.size()); mAppPackageName = appPackageName; for (LocaleStore.LocaleInfo li : localeOptions) { if (li.isSuggested()) { mSuggestionCount++; Loading @@ -83,7 +92,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public boolean isEnabled(int position) { return getItemViewType(position) == TYPE_LOCALE; return getItemViewType(position) == TYPE_LOCALE || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; } @Override Loading @@ -97,13 +107,20 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { if (position == mSuggestionCount + 1) { return TYPE_HEADER_ALL_OTHERS; } LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); if (item.isSystemLocale()) { return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; } return TYPE_LOCALE; } } @Override public int getViewTypeCount() { if (showHeaders()) { if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) { return 4; // Two headers and 1 for "System language" } else if (showHeaders()) { return 3; // Two headers in addition to the locales } else { return 1; // Locales items only Loading Loading @@ -187,6 +204,21 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { textView.setTextLocale( mDisplayLocale != null ? mDisplayLocale : Locale.getDefault()); break; case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { convertView = mInflater.inflate( R.layout.app_language_picker_system_default, parent, false); } Locale defaultLocale = Locale.getDefault(); TextView title = convertView.findViewById(R.id.locale); title.setText(R.string.system_locale_title); title.setTextLocale(defaultLocale); TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle); subtitle.setText(defaultLocale.getDisplayName()); subtitle.setTextLocale(defaultLocale); break; default: // Covers both null, and "reusing" a wrong kind of view if (!(convertView instanceof ViewGroup)) { Loading Loading @@ -316,4 +348,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { public Filter getFilter() { return new FilterByNativeAndUiNames(); } public String getAppPackageName() { return mAppPackageName; } } Loading
core/java/com/android/internal/app/AppLocaleStore.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * 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 android.app.LocaleConfig; import android.content.Context; import android.content.pm.PackageManager; import android.os.LocaleList; import android.util.Log; import java.util.ArrayList; import java.util.Locale; public class AppLocaleStore { private static final String TAG = AppLocaleStore.class.getSimpleName(); public static ArrayList<Locale> getAppSupportedLocales(Context context, String packageName) { ArrayList<Locale> appSupportedLocales = new ArrayList<>(); LocaleList packageLocaleList = getPackageLocales(context, packageName); if (packageLocaleList != null && packageLocaleList.size() > 0) { for (int i = 0; i < packageLocaleList.size(); i++) { appSupportedLocales.add(packageLocaleList.get(i)); } Log.d(TAG, "getAppSupportedLocales from LocaleConfig. Size: " + appSupportedLocales.size()); } else { String[] languages = getAssetLocales(context, packageName); for (String language : languages) { appSupportedLocales.add(Locale.forLanguageTag(language)); } Log.d(TAG, "getAppSupportedLocales from asset. Size: " + appSupportedLocales.size()); } return appSupportedLocales; } private static LocaleList getPackageLocales(Context context, String packageName) { try { LocaleConfig localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0)); if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { return localeConfig.getSupportedLocales(); } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } return null; } private static String[] getAssetLocales(Context context, String packageName) { try { PackageManager packageManager = context.getPackageManager(); String[] locales = packageManager.getResourcesForApplication( packageManager.getPackageInfo(packageName, PackageManager.MATCH_ALL) .applicationInfo).getAssets().getNonSystemLocales(); if (locales == null) { Log.i(TAG, "[" + packageName + "] locales are null."); return new String[0]; } else if (locales.length <= 0) { Log.i(TAG, "[" + packageName + "] locales length is 0."); return new String[0]; } return locales; } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } return new String[0]; } }
core/java/com/android/internal/app/LocaleHelper.java +5 −1 Original line number Diff line number Diff line Loading @@ -234,7 +234,11 @@ public class LocaleHelper { public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) { // We don't care about the various suggestion types, just "suggested" (!= 0) // and "all others" (== 0) if (lhs.isSuggested() == rhs.isSuggested()) { if (lhs.isAppCurrentLocale() || rhs.isAppCurrentLocale()) { return lhs.isAppCurrentLocale() ? -1 : 1; } else if (lhs.isSystemLocale() || rhs.isSystemLocale()) { return lhs.isSystemLocale() ? -1 : 1; } else if (lhs.isSuggested() == rhs.isSuggested()) { // They are in the same "bucket" (suggested / others), so we compare the text return mCollator.compare( removePrefixForCompare(lhs.getLocale(), lhs.getLabel(mCountryMode)), Loading
core/java/com/android/internal/app/LocalePickerWithRegion.java +68 −10 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.os.Bundle; import android.os.LocaleList; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; Loading @@ -32,6 +33,7 @@ import android.widget.SearchView; import com.android.internal.R; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Locale; Loading @@ -45,6 +47,7 @@ import java.util.Set; * default locale.</p> */ public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener { private static final String TAG = LocalePickerWithRegion.class.getSimpleName(); private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; private SuggestedLocaleAdapter mAdapter; Loading @@ -57,6 +60,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O private boolean mPreviousSearchHadFocus = false; private int mFirstVisiblePosition = 0; private int mTopDistance = 0; private String mAppPackageName; /** * Other classes can register to be notified when a locale was selected. Loading @@ -73,17 +77,25 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O private static LocalePickerWithRegion createCountryPicker(Context context, LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, boolean translatedOnly) { boolean translatedOnly, String appPackageName) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); boolean shouldShowTheList = localePicker.setListener(context, listener, parent, translatedOnly); translatedOnly, appPackageName); return shouldShowTheList ? localePicker : null; } public static LocalePickerWithRegion createLanguagePicker(Context context, LocaleSelectedListener listener, boolean translatedOnly) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); localePicker.setListener(context, listener, /* parent */ null, translatedOnly); localePicker.setListener(context, listener, /* parent */ null, translatedOnly, null); return localePicker; } public static LocalePickerWithRegion createLanguagePicker(Context context, LocaleSelectedListener listener, boolean translatedOnly, String appPackageName) { LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); localePicker.setListener( context, listener, /* parent */ null, translatedOnly, appPackageName); return localePicker; } Loading @@ -101,20 +113,32 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O * "pretending" it was selected, and return false.</p> */ private boolean setListener(Context context, LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, boolean translatedOnly) { LocaleStore.LocaleInfo parent, boolean translatedOnly, String appPackageName) { this.mParentLocale = parent; this.mListener = listener; this.mTranslatedOnly = translatedOnly; this.mAppPackageName = appPackageName; setRetainInstance(true); final HashSet<String> langTagsToIgnore = new HashSet<>(); if (!translatedOnly) { LocaleStore.LocaleInfo appCurrentLocale = LocaleStore.getAppCurrentLocaleInfo(context, appPackageName); boolean isForCountryMode = parent != null; if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) { if (appCurrentLocale != null) { Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag()); langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag()); } else { Log.d(TAG, "appCurrentLocale is null"); } } else if (!translatedOnly) { final LocaleList userLocales = LocalePicker.getLocales(); final String[] langTags = userLocales.toLanguageTags().split(","); Collections.addAll(langTagsToIgnore, langTags); } if (parent != null) { if (isForCountryMode) { mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, parent, translatedOnly); if (mLocaleList.size() <= 1) { Loading @@ -127,10 +151,39 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, null /* no parent */, translatedOnly); } Log.d(TAG, "mLocaleList size: " + mLocaleList.size()); // Adding current locale and system default option into suggestion list if(!TextUtils.isEmpty(appPackageName)) { if (appCurrentLocale != null && !isForCountryMode) { mLocaleList.add(appCurrentLocale); } filterTheLanguagesNotSupportedInApp(context, appPackageName); if (!isForCountryMode) { mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo()); } } return true; } private void filterTheLanguagesNotSupportedInApp(Context context, String appPackageName) { ArrayList<Locale> supportedLocales = AppLocaleStore.getAppSupportedLocales(context, appPackageName); Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>(); for(LocaleStore.LocaleInfo li: mLocaleList) { for(Locale l: supportedLocales) { if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) { filteredList.add(li); } } } Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); mLocaleList = filteredList; } private void returnToParentFrame() { getFragmentManager().popBackStack(PARENT_FRAGMENT_NAME, FragmentManager.POP_BACK_STACK_INCLUSIVE); Loading @@ -151,7 +204,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O final boolean countryMode = mParentLocale != null; final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault(); mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode); mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, mAppPackageName); final LocaleHelper.LocaleInfoComparator comp = new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode); mAdapter.sort(comp); Loading Loading @@ -212,17 +265,22 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O @Override public void onListItemClick(ListView l, View v, int position, long id) { SuggestedLocaleAdapter adapter = (SuggestedLocaleAdapter) getListAdapter(); final LocaleStore.LocaleInfo locale = (LocaleStore.LocaleInfo) getListAdapter().getItem(position); (LocaleStore.LocaleInfo) adapter.getItem(position); // Special case for resetting the app locale to equal the system locale. boolean isSystemLocale = locale.isSystemLocale(); boolean isRegionLocale = locale.getParent() != null; if (locale.getParent() != null) { if (isSystemLocale || isRegionLocale) { if (mListener != null) { mListener.onLocaleSelected(locale); } returnToParentFrame(); } else { LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker( getContext(), mListener, locale, mTranslatedOnly /* translate only */); getContext(), mListener, locale, mTranslatedOnly /* translate only */, adapter.getAppPackageName()); if (selector != null) { getFragmentManager().beginTransaction() .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) Loading
core/java/com/android/internal/app/LocaleStore.java +49 −0 Original line number Diff line number Diff line Loading @@ -16,11 +16,13 @@ package com.android.internal.app; import android.app.LocaleManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.LocaleList; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; import java.io.Serializable; import java.util.HashMap; Loading @@ -31,12 +33,17 @@ import java.util.Set; public class LocaleStore { private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>(); private static final String TAG = LocaleStore.class.getSimpleName(); private static boolean sFullyInitialized = false; public static class LocaleInfo implements Serializable { private static final int SUGGESTION_TYPE_NONE = 0; private static final int SUGGESTION_TYPE_SIM = 1 << 0; private static final int SUGGESTION_TYPE_CFG = 1 << 1; // Only for per-app language picker private static final int SUGGESTION_TYPE_CURRENT = 1 << 2; // Only for per-app language picker private static final int SUGGESTION_TYPE_SYSTEM_LANGUAGE = 1 << 3; private final Locale mLocale; private final Locale mParent; Loading Loading @@ -189,6 +196,14 @@ public class LocaleStore { public void setChecked(boolean checked) { mIsChecked = checked; } public boolean isAppCurrentLocale() { return (mSuggestionFlags & SUGGESTION_TYPE_CURRENT) > 0; } public boolean isSystemLocale() { return (mSuggestionFlags & SUGGESTION_TYPE_SYSTEM_LANGUAGE) > 0; } } private static Set<String> getSimCountries(Context context) { Loading Loading @@ -239,6 +254,40 @@ public class LocaleStore { } } public static LocaleInfo getAppCurrentLocaleInfo(Context context, String appPackageName) { if (appPackageName == null) { return null; } LocaleManager localeManager = context.getSystemService(LocaleManager.class); try { LocaleList localeList = (localeManager == null) ? null : localeManager.getApplicationLocales(appPackageName); Locale locale = localeList == null ? null : localeList.get(0); if (locale != null) { LocaleInfo localeInfo = new LocaleInfo(locale); localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT; localeInfo.mIsTranslated = true; return localeInfo; } } catch (IllegalArgumentException e) { Log.d(TAG, "IllegalArgumentException ", e); } return null; } /** * The "system default" is special case for per-app picker. Intentionally keep the locale * empty to let activity know "system default" been selected. */ public static LocaleInfo getSystemDefaultLocaleInfo() { LocaleInfo systemDefaultInfo = new LocaleInfo(""); systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE; systemDefaultInfo.mIsTranslated = true; return systemDefaultInfo; } /* * Show all the languages supported for a country in the suggested list. * This is also handy for devices without SIM (tablets). Loading
core/java/com/android/internal/app/SuggestedLocaleAdapter.java +38 −2 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private static final int TYPE_HEADER_SUGGESTED = 0; private static final int TYPE_HEADER_ALL_OTHERS = 1; private static final int TYPE_LOCALE = 2; private static final int TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER = 3; private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6; private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions; Loading @@ -64,10 +65,18 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private Locale mDisplayLocale = null; // used to potentially cache a modified Context that uses mDisplayLocale private Context mContextOverride = null; private String mAppPackageName; public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) { this(localeOptions, countryMode, null); } public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode, String appPackageName) { mCountryMode = countryMode; mLocaleOptions = new ArrayList<>(localeOptions.size()); mAppPackageName = appPackageName; for (LocaleStore.LocaleInfo li : localeOptions) { if (li.isSuggested()) { mSuggestionCount++; Loading @@ -83,7 +92,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public boolean isEnabled(int position) { return getItemViewType(position) == TYPE_LOCALE; return getItemViewType(position) == TYPE_LOCALE || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; } @Override Loading @@ -97,13 +107,20 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { if (position == mSuggestionCount + 1) { return TYPE_HEADER_ALL_OTHERS; } LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); if (item.isSystemLocale()) { return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; } return TYPE_LOCALE; } } @Override public int getViewTypeCount() { if (showHeaders()) { if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) { return 4; // Two headers and 1 for "System language" } else if (showHeaders()) { return 3; // Two headers in addition to the locales } else { return 1; // Locales items only Loading Loading @@ -187,6 +204,21 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { textView.setTextLocale( mDisplayLocale != null ? mDisplayLocale : Locale.getDefault()); break; case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { convertView = mInflater.inflate( R.layout.app_language_picker_system_default, parent, false); } Locale defaultLocale = Locale.getDefault(); TextView title = convertView.findViewById(R.id.locale); title.setText(R.string.system_locale_title); title.setTextLocale(defaultLocale); TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle); subtitle.setText(defaultLocale.getDisplayName()); subtitle.setTextLocale(defaultLocale); break; default: // Covers both null, and "reusing" a wrong kind of view if (!(convertView instanceof ViewGroup)) { Loading Loading @@ -316,4 +348,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { public Filter getFilter() { return new FilterByNativeAndUiNames(); } public String getAppPackageName() { return mAppPackageName; } }