Loading res/values/strings.xml +12 −1 Original line number Diff line number Diff line Loading @@ -424,6 +424,9 @@ <!-- Category for the locale picker. [CHAR LIMIT=50]--> <string name="all_supported_locales_title">All languages</string> <!-- Category for the app locale picker. [CHAR LIMIT=50]--> <string name="more_supported_locales_title">More languages</string> <!-- Category for the locale region picker. [CHAR LIMIT=50]--> <string name="all_supported_locales_regions_title">All regions</string> Loading Loading @@ -480,12 +483,20 @@ <!-- Menu item in the locale menu [CHAR LIMIT=30] --> <string name="locale_search_menu">Search</string> <!-- Title for the language and region selection screen [CHAR LIMIT=25] --> <string name="language_and_region_title">Language & region</string> <!-- Title for the language selection screen [CHAR LIMIT=25] --> <string name="language_selection_title">Add a language</string> <!-- Title for the region picker [CHAR LIMIT=25] --> <string name="region_selection_title">Choose a region</string> <!-- Title for the region selection screen [CHAR LIMIT=25] --> <string name="country_selection_title">Region preference</string> <!-- Hint text in a search edit box (used to filter long language / country lists) [CHAR LIMIT=25] --> <string name="search_language_hint">Type language name</string> <string name="search_language_hint">Search languages</string> <!-- Hint text in a search edit box (used to filter long region) [CHAR LIMIT=25] --> <string name="search_region_hint">Search regions</string> <!-- The title for Choose a region page --> <string name="top_intro_region_title">The region you choose affects how your phone displays time, dates, temperature, and more</string> <!-- Category for more language settings. [CHAR LIMIT=NONE]--> <string name="more_language_settings_category">More language settings</string> res/xml/system_language_picker.xml +6 −0 Original line number Diff line number Diff line Loading @@ -16,9 +16,15 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/language_selection_title" android:key="key_system_language_picker_page"> <com.android.settingslib.widget.TopIntroPreference android:key="top_intro_region" android:title="@string/top_intro_region_title" settings:isPreferenceVisible="false"/> <PreferenceCategory android:key="system_language_suggested_category" android:title="@string/suggested_locales_title"/> Loading src/com/android/settings/localepicker/LocaleListSearchCallback.java +3 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.settings.localepicker; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.app.LocaleStore; Loading @@ -25,5 +26,6 @@ import java.util.List; public interface LocaleListSearchCallback { /** Callback method for searching changes. */ void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> localeInfoList); void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> localeInfoList, @Nullable CharSequence prefix); } src/com/android/settings/localepicker/LocalePickerBaseListPreferenceController.java +43 −15 Original line number Diff line number Diff line Loading @@ -60,7 +60,9 @@ public abstract class LocalePickerBaseListPreferenceController extends private Map<String, Preference> mPreferences; private String mPackageName; private boolean mIsCountryMode; @Nullable private LocaleStore.LocaleInfo mParentLocale; @Nullable private LocaleStore.LocaleInfo mParentLocale; private boolean mIsSuggestedCategory; public LocalePickerBaseListPreferenceController(@NonNull Context context, @NonNull String preferenceKey) { Loading @@ -75,6 +77,7 @@ public abstract class LocalePickerBaseListPreferenceController extends public void displayPreference(@NonNull PreferenceScreen screen) { super.displayPreference(screen); mPreferenceCategory = screen.findPreference(getPreferenceCategoryKey()); mIsSuggestedCategory = getPreferenceCategoryKey().contains(KEY_SUGGESTED); updatePreferences(); } Loading @@ -88,17 +91,13 @@ public abstract class LocalePickerBaseListPreferenceController extends mParentLocale = getParentLocale(); if (mParentLocale != null) { mIsCountryMode = true; mLocaleList = getLocaleCollectorController(mContext).getSupportedLocaleList( mParentLocale, false, mIsCountryMode); mLocaleOptions = new ArrayList<>(mLocaleList.size()); if (!getPreferenceCategoryKey().contains(KEY_SUGGESTED)) { if (!mIsSuggestedCategory) { mPreferenceCategory.setTitle( mContext.getString(R.string.all_supported_locales_regions_title)); } } result = getSortedLocaleList( getPreferenceCategoryKey().contains(KEY_SUGGESTED) result = getSortedLocaleList(mIsSuggestedCategory ? getSuggestedLocaleList() : getSupportedLocaleList()); Loading @@ -112,13 +111,17 @@ public abstract class LocalePickerBaseListPreferenceController extends } @Override public void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> newList) { public void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> newList, @Nullable CharSequence prefix) { mPreferenceCategory.removeAll(); mPreferences.clear(); final Map<String, Preference> existingPreferences = mPreferences; if (getPreferenceCategoryKey().contains(KEY_SUGGESTED)) { newList = getSortedSuggestedLocaleFromSearchList( newList, getSuggestedLocaleList()); List<LocaleStore.LocaleInfo> sortedList = mIsSuggestedCategory ? getSuggestedLocaleList() : getSupportedLocaleList(); newList = getSortedSuggestedLocaleFromSearchList(newList, sortedList); if (mIsSuggestedCategory && getParentLocale() != null) { newList = getSortedSuggestedRegionFromSearchList(prefix, newList, sortedList); } setupPreference(newList, existingPreferences); } Loading @@ -138,6 +141,23 @@ public abstract class LocalePickerBaseListPreferenceController extends return searchItem; } private List<LocaleStore.LocaleInfo> getSortedSuggestedRegionFromSearchList( @Nullable CharSequence prefix, List<LocaleStore.LocaleInfo> listOptions, List<LocaleStore.LocaleInfo> listSuggested) { List<LocaleStore.LocaleInfo> searchItem = new ArrayList<>(); if (prefix == null || prefix.isEmpty()) { return getSortedLocaleList(listSuggested); } for (LocaleStore.LocaleInfo option : listOptions) { if (listSuggested.contains(option)) { searchItem.add(option); } } return getSortedLocaleList(searchItem); } private void setupPreference(List<LocaleStore.LocaleInfo> localeInfoList, Map<String, Preference> existingPreferences) { Log.d(TAG, "setupPreference: isNumberingMode = " + isNumberingMode()); Loading Loading @@ -175,18 +195,20 @@ public abstract class LocalePickerBaseListPreferenceController extends protected abstract LocaleCollectorBase getLocaleCollectorController(Context context); @Nullable protected abstract LocaleStore.LocaleInfo getParentLocale(); @Nullable protected abstract LocaleStore.LocaleInfo getParentLocale(); protected abstract boolean isNumberingMode(); @Nullable protected abstract LocaleList getExplicitLocaleList(); @Nullable protected abstract LocaleList getExplicitLocaleList(); protected String getPackageName() { return mPackageName; } protected List<LocaleStore.LocaleInfo> getSuggestedLocaleList() { mLocaleOptions.clear(); setupLocaleList(); if (mLocaleList != null && !mLocaleList.isEmpty()) { mLocaleOptions.addAll(mLocaleList.stream() .filter(localeInfo -> localeInfo.isSuggested()) Loading @@ -199,6 +221,7 @@ public abstract class LocalePickerBaseListPreferenceController extends } protected List<LocaleStore.LocaleInfo> getSupportedLocaleList() { setupLocaleList(); if (mLocaleList != null && !mLocaleList.isEmpty()) { mLocaleOptions.addAll(mLocaleList.stream() .filter(localeInfo -> !localeInfo.isSuggested()) Loading @@ -206,10 +229,15 @@ public abstract class LocalePickerBaseListPreferenceController extends } else { Log.d(TAG, "Can not get supported locales because the locale list is null or empty."); } return mLocaleOptions; } private void setupLocaleList() { mLocaleList = getLocaleCollectorController(mContext).getSupportedLocaleList( mParentLocale, false, mIsCountryMode); mLocaleOptions.clear(); } private List<LocaleStore.LocaleInfo> getSortedLocaleList( List<LocaleStore.LocaleInfo> localeInfos) { final Locale sortingLocale = Locale.getDefault(); Loading src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java +190 −11 Original line number Diff line number Diff line Loading @@ -19,33 +19,39 @@ package com.android.settings.localepicker; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Filter; import android.widget.SearchView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceCategory; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.app.LocaleHelper; import com.android.internal.app.LocaleStore; import com.android.internal.app.SystemLocaleCollector; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.PreferenceCategoryController; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.TopIntroPreference; import com.google.android.material.appbar.AppBarLayout; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A locale picker fragment to show region country and numbering system. Loading @@ -54,7 +60,8 @@ import java.util.Set; * Allows the user to search for locales using both their native name and their name in the * default locale.</p> */ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { public class RegionAndNumberingSystemPickerFragment extends DashboardFragment implements SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { public static final String EXTRA_TARGET_LOCALE = "extra_target_locale"; public static final String EXTRA_IS_NUMBERING_SYSTEM = "extra_is_numbering_system"; Loading @@ -63,17 +70,30 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { private static final String KEY_PREFERENCE_SYSTEM_LOCALE_LIST = "system_locale_list"; private static final String KEY_PREFERENCE_SYSTEM_LOCALE_SUGGESTED_LIST = "system_locale_suggested_list"; private static final String KEY_TOP_INTRO_PREFERENCE = "top_intro_region"; private static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view"; @Nullable private SystemLocaleAllListPreferenceController mSystemLocaleAllListPreferenceController; private SearchView mSearchView = null; @Nullable private SearchFilter mSearchFilter = null; @SuppressWarnings("NullAway") private SystemLocaleAllListPreferenceController mSystemLocaleAllListPreferenceController; @SuppressWarnings("NullAway") private SystemLocaleSuggestedListPreferenceController mSuggestedListPreferenceController; @Nullable private LocaleStore.LocaleInfo mLocaleInfo; private RecyclerView mRecyclerView; @Nullable private List<LocaleStore.LocaleInfo> mLocaleOptions; @SuppressWarnings("NullAway") private List<LocaleStore.LocaleInfo> mOriginalLocaleInfos; private AppBarLayout mAppBarLayout; private RecyclerView mRecyclerView; private Activity mActivity; private boolean mExpandSearch; private boolean mIsNumberingMode; @Nullable private CharSequence mPrefix; @Override public void onCreate(@NonNull Bundle icicle) { Loading @@ -83,13 +103,27 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { Log.d(TAG, "onCreate, no activity or activity is finishing"); return; } setHasOptionsMenu(true); if (mLocaleInfo == null) { Log.d(TAG, "onCreate, can not get localeInfo"); return; mExpandSearch = mActivity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false); if (icicle != null) { mExpandSearch = icicle.getBoolean(EXTRA_EXPAND_SEARCH_VIEW); } Log.d(TAG, "onCreate, mIsNumberingMode = " + mIsNumberingMode); if (!mIsNumberingMode) { mActivity.setTitle(R.string.region_selection_title); } mActivity.setTitle(mLocaleInfo.getFullNameNative()); TopIntroPreference topIntroPreference = findPreference(KEY_TOP_INTRO_PREFERENCE); if (topIntroPreference != null) { topIntroPreference.setVisible(!mIsNumberingMode); } if (mSystemLocaleAllListPreferenceController != null) { mOriginalLocaleInfos = mSystemLocaleAllListPreferenceController.getSupportedLocaleList(); } } @Override Loading @@ -106,6 +140,151 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { mRecyclerView = view.findViewById(R.id.recycler_view); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); if (mSearchView != null) { outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified()); } } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.language_selection_list, menu); final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu); if (searchMenuItem != null) { searchMenuItem.setOnActionExpandListener(this); mSearchView = (SearchView) searchMenuItem.getActionView(); mSearchView.setQueryHint( getContext().getResources().getText(R.string.search_region_hint)); mSearchView.setOnQueryTextListener(this); mSearchView.setMaxWidth(Integer.MAX_VALUE); if (mExpandSearch) { searchMenuItem.expandActionView(); } } } private void filterSearch(@Nullable String query) { if (mSearchFilter == null) { mSearchFilter = new SearchFilter(); } // If we haven't load apps list completely, don't filter anything. if (mOriginalLocaleInfos == null) { Log.w(TAG, "Locales haven't loaded completely yet, so nothing can be filtered"); return; } mSearchFilter.filter(query); } private class SearchFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { FilterResults results = new FilterResults(); mPrefix = prefix; if (TextUtils.isEmpty(prefix)) { results.values = mOriginalLocaleInfos; results.count = mOriginalLocaleInfos.size(); } else { // TODO: decide if we should use the string's locale List<LocaleStore.LocaleInfo> newList = new ArrayList<>(mOriginalLocaleInfos); newList.addAll(mSystemLocaleAllListPreferenceController.getSuggestedLocaleList()); Locale locale = Locale.getDefault(); String prefixString = LocaleHelper.normalizeForSearch(prefix.toString(), locale); final int count = newList.size(); final ArrayList<LocaleStore.LocaleInfo> newValues = new ArrayList<>(); for (int i = 0; i < count; i++) { final LocaleStore.LocaleInfo value = newList.get(i); final String nameToCheck = LocaleHelper.normalizeForSearch( value.getFullNameInUiLanguage(), locale); final String nativeNameToCheck = LocaleHelper.normalizeForSearch( value.getFullNameNative(), locale); if ((wordMatches(nativeNameToCheck, prefixString) || wordMatches(nameToCheck, prefixString)) && !newValues.contains( value)) { newValues.add(value); } } results.values = newValues; results.count = newValues.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if (mSystemLocaleAllListPreferenceController == null || mSuggestedListPreferenceController == null) { Log.d(TAG, "publishResults(), can not get preference."); return; } mLocaleOptions = (ArrayList<LocaleStore.LocaleInfo>) results.values; // TODO: Need to scroll to first preference when searching. if (mRecyclerView != null) { mRecyclerView.post(() -> mRecyclerView.scrollToPosition(0)); } mSystemLocaleAllListPreferenceController.onSearchListChanged(mLocaleOptions, mPrefix); mSuggestedListPreferenceController.onSearchListChanged(mLocaleOptions, mPrefix); } // TODO: decide if this is enough, or we want to use a BreakIterator... private boolean wordMatches(String valueText, String prefixString) { if (valueText == null) { return false; } // First match against the whole, non-split value if (valueText.startsWith(prefixString)) { return true; } // For example: English (Australia), Arabic (Egypt) Pattern pattern = Pattern.compile("^.*?\\((.*)"); Matcher matcher = pattern.matcher(valueText); if (matcher.find()) { String region = matcher.group(1); return region.startsWith(prefixString); } return false; } } @Override public boolean onMenuItemActionExpand(@NonNull MenuItem item) { // To prevent a large space on tool bar. mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); // To prevent user can expand the collapsing tool bar view. ViewCompat.setNestedScrollingEnabled(mRecyclerView, false); return true; } @Override public boolean onMenuItemActionCollapse(@NonNull MenuItem item) { // We keep the collapsed status after user cancel the search function. mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); ViewCompat.setNestedScrollingEnabled(mRecyclerView, true); return true; } @Override public boolean onQueryTextSubmit(@Nullable String query) { return false; } @Override public boolean onQueryTextChange(@Nullable String newText) { filterSearch(newText); return false; } @Override protected String getLogTag() { return TAG; Loading Loading
res/values/strings.xml +12 −1 Original line number Diff line number Diff line Loading @@ -424,6 +424,9 @@ <!-- Category for the locale picker. [CHAR LIMIT=50]--> <string name="all_supported_locales_title">All languages</string> <!-- Category for the app locale picker. [CHAR LIMIT=50]--> <string name="more_supported_locales_title">More languages</string> <!-- Category for the locale region picker. [CHAR LIMIT=50]--> <string name="all_supported_locales_regions_title">All regions</string> Loading Loading @@ -480,12 +483,20 @@ <!-- Menu item in the locale menu [CHAR LIMIT=30] --> <string name="locale_search_menu">Search</string> <!-- Title for the language and region selection screen [CHAR LIMIT=25] --> <string name="language_and_region_title">Language & region</string> <!-- Title for the language selection screen [CHAR LIMIT=25] --> <string name="language_selection_title">Add a language</string> <!-- Title for the region picker [CHAR LIMIT=25] --> <string name="region_selection_title">Choose a region</string> <!-- Title for the region selection screen [CHAR LIMIT=25] --> <string name="country_selection_title">Region preference</string> <!-- Hint text in a search edit box (used to filter long language / country lists) [CHAR LIMIT=25] --> <string name="search_language_hint">Type language name</string> <string name="search_language_hint">Search languages</string> <!-- Hint text in a search edit box (used to filter long region) [CHAR LIMIT=25] --> <string name="search_region_hint">Search regions</string> <!-- The title for Choose a region page --> <string name="top_intro_region_title">The region you choose affects how your phone displays time, dates, temperature, and more</string> <!-- Category for more language settings. [CHAR LIMIT=NONE]--> <string name="more_language_settings_category">More language settings</string>
res/xml/system_language_picker.xml +6 −0 Original line number Diff line number Diff line Loading @@ -16,9 +16,15 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/language_selection_title" android:key="key_system_language_picker_page"> <com.android.settingslib.widget.TopIntroPreference android:key="top_intro_region" android:title="@string/top_intro_region_title" settings:isPreferenceVisible="false"/> <PreferenceCategory android:key="system_language_suggested_category" android:title="@string/suggested_locales_title"/> Loading
src/com/android/settings/localepicker/LocaleListSearchCallback.java +3 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.settings.localepicker; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.app.LocaleStore; Loading @@ -25,5 +26,6 @@ import java.util.List; public interface LocaleListSearchCallback { /** Callback method for searching changes. */ void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> localeInfoList); void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> localeInfoList, @Nullable CharSequence prefix); }
src/com/android/settings/localepicker/LocalePickerBaseListPreferenceController.java +43 −15 Original line number Diff line number Diff line Loading @@ -60,7 +60,9 @@ public abstract class LocalePickerBaseListPreferenceController extends private Map<String, Preference> mPreferences; private String mPackageName; private boolean mIsCountryMode; @Nullable private LocaleStore.LocaleInfo mParentLocale; @Nullable private LocaleStore.LocaleInfo mParentLocale; private boolean mIsSuggestedCategory; public LocalePickerBaseListPreferenceController(@NonNull Context context, @NonNull String preferenceKey) { Loading @@ -75,6 +77,7 @@ public abstract class LocalePickerBaseListPreferenceController extends public void displayPreference(@NonNull PreferenceScreen screen) { super.displayPreference(screen); mPreferenceCategory = screen.findPreference(getPreferenceCategoryKey()); mIsSuggestedCategory = getPreferenceCategoryKey().contains(KEY_SUGGESTED); updatePreferences(); } Loading @@ -88,17 +91,13 @@ public abstract class LocalePickerBaseListPreferenceController extends mParentLocale = getParentLocale(); if (mParentLocale != null) { mIsCountryMode = true; mLocaleList = getLocaleCollectorController(mContext).getSupportedLocaleList( mParentLocale, false, mIsCountryMode); mLocaleOptions = new ArrayList<>(mLocaleList.size()); if (!getPreferenceCategoryKey().contains(KEY_SUGGESTED)) { if (!mIsSuggestedCategory) { mPreferenceCategory.setTitle( mContext.getString(R.string.all_supported_locales_regions_title)); } } result = getSortedLocaleList( getPreferenceCategoryKey().contains(KEY_SUGGESTED) result = getSortedLocaleList(mIsSuggestedCategory ? getSuggestedLocaleList() : getSupportedLocaleList()); Loading @@ -112,13 +111,17 @@ public abstract class LocalePickerBaseListPreferenceController extends } @Override public void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> newList) { public void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> newList, @Nullable CharSequence prefix) { mPreferenceCategory.removeAll(); mPreferences.clear(); final Map<String, Preference> existingPreferences = mPreferences; if (getPreferenceCategoryKey().contains(KEY_SUGGESTED)) { newList = getSortedSuggestedLocaleFromSearchList( newList, getSuggestedLocaleList()); List<LocaleStore.LocaleInfo> sortedList = mIsSuggestedCategory ? getSuggestedLocaleList() : getSupportedLocaleList(); newList = getSortedSuggestedLocaleFromSearchList(newList, sortedList); if (mIsSuggestedCategory && getParentLocale() != null) { newList = getSortedSuggestedRegionFromSearchList(prefix, newList, sortedList); } setupPreference(newList, existingPreferences); } Loading @@ -138,6 +141,23 @@ public abstract class LocalePickerBaseListPreferenceController extends return searchItem; } private List<LocaleStore.LocaleInfo> getSortedSuggestedRegionFromSearchList( @Nullable CharSequence prefix, List<LocaleStore.LocaleInfo> listOptions, List<LocaleStore.LocaleInfo> listSuggested) { List<LocaleStore.LocaleInfo> searchItem = new ArrayList<>(); if (prefix == null || prefix.isEmpty()) { return getSortedLocaleList(listSuggested); } for (LocaleStore.LocaleInfo option : listOptions) { if (listSuggested.contains(option)) { searchItem.add(option); } } return getSortedLocaleList(searchItem); } private void setupPreference(List<LocaleStore.LocaleInfo> localeInfoList, Map<String, Preference> existingPreferences) { Log.d(TAG, "setupPreference: isNumberingMode = " + isNumberingMode()); Loading Loading @@ -175,18 +195,20 @@ public abstract class LocalePickerBaseListPreferenceController extends protected abstract LocaleCollectorBase getLocaleCollectorController(Context context); @Nullable protected abstract LocaleStore.LocaleInfo getParentLocale(); @Nullable protected abstract LocaleStore.LocaleInfo getParentLocale(); protected abstract boolean isNumberingMode(); @Nullable protected abstract LocaleList getExplicitLocaleList(); @Nullable protected abstract LocaleList getExplicitLocaleList(); protected String getPackageName() { return mPackageName; } protected List<LocaleStore.LocaleInfo> getSuggestedLocaleList() { mLocaleOptions.clear(); setupLocaleList(); if (mLocaleList != null && !mLocaleList.isEmpty()) { mLocaleOptions.addAll(mLocaleList.stream() .filter(localeInfo -> localeInfo.isSuggested()) Loading @@ -199,6 +221,7 @@ public abstract class LocalePickerBaseListPreferenceController extends } protected List<LocaleStore.LocaleInfo> getSupportedLocaleList() { setupLocaleList(); if (mLocaleList != null && !mLocaleList.isEmpty()) { mLocaleOptions.addAll(mLocaleList.stream() .filter(localeInfo -> !localeInfo.isSuggested()) Loading @@ -206,10 +229,15 @@ public abstract class LocalePickerBaseListPreferenceController extends } else { Log.d(TAG, "Can not get supported locales because the locale list is null or empty."); } return mLocaleOptions; } private void setupLocaleList() { mLocaleList = getLocaleCollectorController(mContext).getSupportedLocaleList( mParentLocale, false, mIsCountryMode); mLocaleOptions.clear(); } private List<LocaleStore.LocaleInfo> getSortedLocaleList( List<LocaleStore.LocaleInfo> localeInfos) { final Locale sortingLocale = Locale.getDefault(); Loading
src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java +190 −11 Original line number Diff line number Diff line Loading @@ -19,33 +19,39 @@ package com.android.settings.localepicker; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Filter; import android.widget.SearchView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceCategory; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.app.LocaleHelper; import com.android.internal.app.LocaleStore; import com.android.internal.app.SystemLocaleCollector; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.PreferenceCategoryController; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.TopIntroPreference; import com.google.android.material.appbar.AppBarLayout; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A locale picker fragment to show region country and numbering system. Loading @@ -54,7 +60,8 @@ import java.util.Set; * Allows the user to search for locales using both their native name and their name in the * default locale.</p> */ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { public class RegionAndNumberingSystemPickerFragment extends DashboardFragment implements SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { public static final String EXTRA_TARGET_LOCALE = "extra_target_locale"; public static final String EXTRA_IS_NUMBERING_SYSTEM = "extra_is_numbering_system"; Loading @@ -63,17 +70,30 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { private static final String KEY_PREFERENCE_SYSTEM_LOCALE_LIST = "system_locale_list"; private static final String KEY_PREFERENCE_SYSTEM_LOCALE_SUGGESTED_LIST = "system_locale_suggested_list"; private static final String KEY_TOP_INTRO_PREFERENCE = "top_intro_region"; private static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view"; @Nullable private SystemLocaleAllListPreferenceController mSystemLocaleAllListPreferenceController; private SearchView mSearchView = null; @Nullable private SearchFilter mSearchFilter = null; @SuppressWarnings("NullAway") private SystemLocaleAllListPreferenceController mSystemLocaleAllListPreferenceController; @SuppressWarnings("NullAway") private SystemLocaleSuggestedListPreferenceController mSuggestedListPreferenceController; @Nullable private LocaleStore.LocaleInfo mLocaleInfo; private RecyclerView mRecyclerView; @Nullable private List<LocaleStore.LocaleInfo> mLocaleOptions; @SuppressWarnings("NullAway") private List<LocaleStore.LocaleInfo> mOriginalLocaleInfos; private AppBarLayout mAppBarLayout; private RecyclerView mRecyclerView; private Activity mActivity; private boolean mExpandSearch; private boolean mIsNumberingMode; @Nullable private CharSequence mPrefix; @Override public void onCreate(@NonNull Bundle icicle) { Loading @@ -83,13 +103,27 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { Log.d(TAG, "onCreate, no activity or activity is finishing"); return; } setHasOptionsMenu(true); if (mLocaleInfo == null) { Log.d(TAG, "onCreate, can not get localeInfo"); return; mExpandSearch = mActivity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false); if (icicle != null) { mExpandSearch = icicle.getBoolean(EXTRA_EXPAND_SEARCH_VIEW); } Log.d(TAG, "onCreate, mIsNumberingMode = " + mIsNumberingMode); if (!mIsNumberingMode) { mActivity.setTitle(R.string.region_selection_title); } mActivity.setTitle(mLocaleInfo.getFullNameNative()); TopIntroPreference topIntroPreference = findPreference(KEY_TOP_INTRO_PREFERENCE); if (topIntroPreference != null) { topIntroPreference.setVisible(!mIsNumberingMode); } if (mSystemLocaleAllListPreferenceController != null) { mOriginalLocaleInfos = mSystemLocaleAllListPreferenceController.getSupportedLocaleList(); } } @Override Loading @@ -106,6 +140,151 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment { mRecyclerView = view.findViewById(R.id.recycler_view); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); if (mSearchView != null) { outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified()); } } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.language_selection_list, menu); final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu); if (searchMenuItem != null) { searchMenuItem.setOnActionExpandListener(this); mSearchView = (SearchView) searchMenuItem.getActionView(); mSearchView.setQueryHint( getContext().getResources().getText(R.string.search_region_hint)); mSearchView.setOnQueryTextListener(this); mSearchView.setMaxWidth(Integer.MAX_VALUE); if (mExpandSearch) { searchMenuItem.expandActionView(); } } } private void filterSearch(@Nullable String query) { if (mSearchFilter == null) { mSearchFilter = new SearchFilter(); } // If we haven't load apps list completely, don't filter anything. if (mOriginalLocaleInfos == null) { Log.w(TAG, "Locales haven't loaded completely yet, so nothing can be filtered"); return; } mSearchFilter.filter(query); } private class SearchFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { FilterResults results = new FilterResults(); mPrefix = prefix; if (TextUtils.isEmpty(prefix)) { results.values = mOriginalLocaleInfos; results.count = mOriginalLocaleInfos.size(); } else { // TODO: decide if we should use the string's locale List<LocaleStore.LocaleInfo> newList = new ArrayList<>(mOriginalLocaleInfos); newList.addAll(mSystemLocaleAllListPreferenceController.getSuggestedLocaleList()); Locale locale = Locale.getDefault(); String prefixString = LocaleHelper.normalizeForSearch(prefix.toString(), locale); final int count = newList.size(); final ArrayList<LocaleStore.LocaleInfo> newValues = new ArrayList<>(); for (int i = 0; i < count; i++) { final LocaleStore.LocaleInfo value = newList.get(i); final String nameToCheck = LocaleHelper.normalizeForSearch( value.getFullNameInUiLanguage(), locale); final String nativeNameToCheck = LocaleHelper.normalizeForSearch( value.getFullNameNative(), locale); if ((wordMatches(nativeNameToCheck, prefixString) || wordMatches(nameToCheck, prefixString)) && !newValues.contains( value)) { newValues.add(value); } } results.values = newValues; results.count = newValues.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if (mSystemLocaleAllListPreferenceController == null || mSuggestedListPreferenceController == null) { Log.d(TAG, "publishResults(), can not get preference."); return; } mLocaleOptions = (ArrayList<LocaleStore.LocaleInfo>) results.values; // TODO: Need to scroll to first preference when searching. if (mRecyclerView != null) { mRecyclerView.post(() -> mRecyclerView.scrollToPosition(0)); } mSystemLocaleAllListPreferenceController.onSearchListChanged(mLocaleOptions, mPrefix); mSuggestedListPreferenceController.onSearchListChanged(mLocaleOptions, mPrefix); } // TODO: decide if this is enough, or we want to use a BreakIterator... private boolean wordMatches(String valueText, String prefixString) { if (valueText == null) { return false; } // First match against the whole, non-split value if (valueText.startsWith(prefixString)) { return true; } // For example: English (Australia), Arabic (Egypt) Pattern pattern = Pattern.compile("^.*?\\((.*)"); Matcher matcher = pattern.matcher(valueText); if (matcher.find()) { String region = matcher.group(1); return region.startsWith(prefixString); } return false; } } @Override public boolean onMenuItemActionExpand(@NonNull MenuItem item) { // To prevent a large space on tool bar. mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); // To prevent user can expand the collapsing tool bar view. ViewCompat.setNestedScrollingEnabled(mRecyclerView, false); return true; } @Override public boolean onMenuItemActionCollapse(@NonNull MenuItem item) { // We keep the collapsed status after user cancel the search function. mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); ViewCompat.setNestedScrollingEnabled(mRecyclerView, true); return true; } @Override public boolean onQueryTextSubmit(@Nullable String query) { return false; } @Override public boolean onQueryTextChange(@Nullable String newText) { filterSearch(newText); return false; } @Override protected String getLogTag() { return TAG; Loading