[Settings] Refactor: Add SystemLocalePickerFragment

Bug: 377664066
Flag: EXEMPT refactor
Change-Id: I79805c639197911d10f3632e50b5feced08c2fd6
This commit is contained in:
Zoey Chen
2024-11-12 13:04:23 +00:00
parent 14ba0b6571
commit a15948b53e
4 changed files with 357 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2024 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/locale_search_menu"
android:title="@string/locale_search_menu"
android:icon="@drawable/ic_search_24dp"
android:showAsAction="always|collapseActionView"
android:actionViewClass="android.widget.SearchView" />
</menu>

View File

@@ -402,6 +402,18 @@
<!-- Category for the app's locale picker activity. [CHAR LIMIT=50]--> <!-- Category for the app's locale picker activity. [CHAR LIMIT=50]-->
<string name="all_supported_app_locales_title">All languages</string> <string name="all_supported_app_locales_title">All languages</string>
<!-- Category for suggested locales. [CHAR LIMIT=50]-->
<string name="suggested_locales_title">Suggested</string>
<!-- Category for the locale picker. [CHAR LIMIT=50]-->
<string name="all_supported_locales_title">All languages</string>
<!-- Category for the locale region picker. [CHAR LIMIT=50]-->
<string name="all_supported_locales_regions_title">All regions</string>
<!-- Category for the numbering system of locale region picker. [CHAR LIMIT=50]-->
<string name="all_supported_numbering_system_title">All numbering systems</string>
<!-- Title for preference of the system default locale. [CHAR LIMIT=50]--> <!-- Title for preference of the system default locale. [CHAR LIMIT=50]-->
<string name="preference_of_system_locale_title">System language</string> <string name="preference_of_system_locale_title">System language</string>
@@ -450,6 +462,15 @@
<!-- The text of the confirmation dialog for saying this selected locale is unavailable to use. [CHAR LIMIT=NONE]--> <!-- The text of the confirmation dialog for saying this selected locale is unavailable to use. [CHAR LIMIT=NONE]-->
<string name="desc_unavailable_locale">This language cant be used as a system language, but youve let apps and websites know you prefer this language.</string> <string name="desc_unavailable_locale">This language cant be used as a system language, but youve let apps and websites know you prefer this language.</string>
<!-- Menu item in the locale menu [CHAR LIMIT=30] -->
<string name="locale_search_menu">Search</string>
<!-- Title for the language selection screen [CHAR LIMIT=25] -->
<string name="language_selection_title">Add a language</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>
<!-- Regional Preferences begin --> <!-- Regional Preferences begin -->
<!-- The title of the menu entry of regional preferences. [CHAR LIMIT=50] --> <!-- The title of the menu entry of regional preferences. [CHAR LIMIT=50] -->
<string name="regional_preferences_title">Regional preferences</string> <string name="regional_preferences_title">Regional preferences</string>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2024 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/language_selection_title"
android:key="key_system_language_picker_page">
<PreferenceCategory
android:key="system_language_suggested_category"
android:title="@string/suggested_locales_title"/>
<PreferenceCategory
android:key="system_language_all_supported_category"
android:title="@string/all_supported_locales_title"/>
</PreferenceScreen>

View File

@@ -0,0 +1,281 @@
/**
* Copyright (C) 2024 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.settings.localepicker;
import android.app.Activity;
import android.app.settings.SettingsEnums;
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.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.google.android.material.appbar.AppBarLayout;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* A locale picker fragment to show system languages.
*
* <p>It shows suggestions at the top, then the rest of the locales.
* Allows the user to search for locales using both their native name and their name in the
* default locale.</p>
*/
public class SystemLocalePickerFragment extends DashboardFragment implements
SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
private static final String TAG = "SystemLocalePickerFragment";
private static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view";
@Nullable private SearchView mSearchView = null;
@Nullable private SearchFilter mSearchFilter = null;
@Nullable private Set<LocaleStore.LocaleInfo> mLocaleList;
@Nullable private List<LocaleStore.LocaleInfo> mLocaleOptions;
@Nullable private List<LocaleStore.LocaleInfo> mOriginalLocaleInfos;
private AppBarLayout mAppBarLayout;
private RecyclerView mRecyclerView;
private Activity mActivity;
private boolean mExpandSearch;
@Override
public void onCreate(@NonNull Bundle icicle) {
super.onCreate(icicle);
mActivity = getActivity();
if (mActivity.isFinishing()) {
return;
}
setHasOptionsMenu(true);
mExpandSearch = mActivity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false);
if (icicle != null) {
mExpandSearch = icicle.getBoolean(EXTRA_EXPAND_SEARCH_VIEW);
}
SystemLocaleCollector systemLocaleCollector = new SystemLocaleCollector(getContext(), null);
mLocaleList = systemLocaleCollector.getSupportedLocaleList(null, false, false);
mLocaleOptions = new ArrayList<>(mLocaleList.size());
}
@Override
public @NonNull View onCreateView(@NonNull LayoutInflater inflater,
@NonNull ViewGroup container, @NonNull Bundle savedInstanceState) {
mAppBarLayout = mActivity.findViewById(R.id.app_bar);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
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_language_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();
}
// TODO: b/30358431 - Add preference of system locales.
// mOriginalLocaleInfos = mSystemLocaleAllListPreferenceController.getSupportedLocaleList();
// 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();
if (mOriginalLocaleInfos == null) {
mOriginalLocaleInfos = new ArrayList<>(mLocaleList);
}
if (TextUtils.isEmpty(prefix)) {
results.values = mOriginalLocaleInfos;
results.count = mOriginalLocaleInfos.size();
} else {
// TODO: decide if we should use the string's locale
Locale locale = Locale.getDefault();
String prefixString = LocaleHelper.normalizeForSearch(prefix.toString(), locale);
final int count = mOriginalLocaleInfos.size();
final ArrayList<LocaleStore.LocaleInfo> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
final LocaleStore.LocaleInfo value = mOriginalLocaleInfos.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) {
mLocaleOptions = (ArrayList<LocaleStore.LocaleInfo>) results.values;
// Need to scroll to first preference when searching.
if (mRecyclerView != null) {
mRecyclerView.post(() -> mRecyclerView.scrollToPosition(0));
}
// TODO: b/30358431 - Add preference of system locales.
// mSystemLocaleAllListPreferenceController.onSearchListChanged(mLocaleOptions);
// mSuggestedListPreferenceController.onSearchListChanged(mLocaleOptions);
}
// 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;
}
return Arrays.stream(valueText.split(" "))
.anyMatch(word -> word.startsWith(prefixString));
}
}
@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;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.USER_LOCALE_LIST;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.system_language_picker;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getSettingsLifecycle());
}
private List<AbstractPreferenceController> buildPreferenceControllers(
@NonNull Context context, @Nullable Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
// TODO: b/30358431 - Add preference of system locales.
return controllers;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.system_language_picker);
}