From 78c1643b92d065e3174f911156a3f41f081d4a8c Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Thu, 26 Dec 2024 10:41:30 +0000 Subject: [PATCH 1/5] [Catalyst] Implement get{Read,Write}Permissions for Vibration & haptics NO_IFTTT=Catalyst only Fix: 385745664 Test: devtool Flag: com.android.settings.flags.catalyst_vibration_intensity_screen Change-Id: I5a6dad4570feb7d313c781ae8e57934ffe417a61 --- .../settings/accessibility/VibrationMainSwitchPreference.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt b/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt index 3265447526b..4f1eec2fcb4 100644 --- a/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt +++ b/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt @@ -48,9 +48,13 @@ class VibrationMainSwitchPreference : override fun storage(context: Context): KeyValueStore = VibrationMainSwitchToggleStorage(SettingsSystemStore.get(context)) + override fun getReadPermissions(context: Context) = SettingsSystemStore.getReadPermissions() + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW + override fun getWritePermissions(context: Context) = SettingsSystemStore.getWritePermissions() + override fun getWritePermit( context: Context, value: Boolean?, From 63215582d56e63bf4fb199b6b1bb64bcedac3a4d Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 27 Dec 2024 21:00:03 +0800 Subject: [PATCH 2/5] [Catalyst] Add back Airplane toggle metrics - The original Airplane toggle metrics is recorded by AirplaneEnabler. Since the design of Catalyst removes AirplaneEnabler, the metrics needs to be added to Catalyst preference. NO_IFTTT=Catalyst only Bug: 386330825 Flag: EXEMPT bugfix Test: Manual testing atest -c AirplaneModePreferenceTest Change-Id: I61f9fc5d623e7d497d13ddf7bc37990ce0fa223e --- .../network/AirplaneModePreference.kt | 5 +++++ .../network/AirplaneModePreferenceTest.kt | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt index f86d41a9e60..11790d0be26 100644 --- a/src/com/android/settings/network/AirplaneModePreference.kt +++ b/src/com/android/settings/network/AirplaneModePreference.kt @@ -17,6 +17,7 @@ package com.android.settings.network import android.app.Activity +import android.app.settings.SettingsEnums.ACTION_AIRPLANE_TOGGLE import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -33,6 +34,7 @@ import com.android.settings.PreferenceRestrictionMixin import com.android.settings.R import com.android.settings.Utils import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn +import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settingslib.RestrictedSwitchPreference import com.android.settingslib.datastore.AbstractKeyedDataObservable import com.android.settingslib.datastore.DataChangeReason @@ -113,6 +115,9 @@ class AirplaneModePreference : val intent = Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) intent.putExtra("state", value) context.sendBroadcastAsUser(intent, UserHandle.ALL) + + val metricsFeature = featureFactory.metricsFeatureProvider + metricsFeature.action(context, ACTION_AIRPLANE_TOGGLE, value) } } diff --git a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt index a0f380fa5ec..75b843d8b46 100644 --- a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt +++ b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt @@ -16,6 +16,7 @@ package com.android.settings.network +import android.app.settings.SettingsEnums.ACTION_AIRPLANE_TOGGLE import android.content.Context import android.content.ContextWrapper import android.content.pm.PackageManager @@ -26,12 +27,14 @@ import android.telephony.TelephonyManager import androidx.preference.SwitchPreferenceCompat import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.testutils.FakeFeatureFactory import com.android.settingslib.datastore.SettingsGlobalStore import com.android.settingslib.preference.createAndBindWidget import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.verify import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.stub @@ -103,6 +106,24 @@ class AirplaneModePreferenceTest { assertThat(getValue).isFalse() } + @Test + fun setValue_valueTrue_metricsActionAirplaneToggleTrue() { + val metricsFeatureProvider = FakeFeatureFactory.setupForTest().metricsFeatureProvider + + airplaneModePreference.storage(context).setBoolean(AirplaneModePreference.KEY, true) + + verify(metricsFeatureProvider).action(context, ACTION_AIRPLANE_TOGGLE, true) + } + + @Test + fun setValue_valueFalse_metricsActionAirplaneToggleFalse() { + val metricsFeatureProvider = FakeFeatureFactory.setupForTest().metricsFeatureProvider + + airplaneModePreference.storage(context).setBoolean(AirplaneModePreference.KEY, false) + + verify(metricsFeatureProvider).action(context, ACTION_AIRPLANE_TOGGLE, false) + } + @Test fun performClick_defaultOn_checkedIsFalse() { SettingsGlobalStore.get(context).setInt(Settings.Global.AIRPLANE_MODE_ON, 1) From 55239c6a03a5d7763e328561cd9da9484d0d34d4 Mon Sep 17 00:00:00 2001 From: tomhsu Date: Mon, 30 Dec 2024 16:40:54 +0000 Subject: [PATCH 3/5] Use AbstractSubscriptionPreferenceController in WifiCallingSettings Flag: EXEMPT task finished Fix: 379611883 Test: atest pass Change-Id: Ic8518ea9f6d5f9004e962e8fb92bc4b4ebc5f313 --- .../settings/wifi/calling/WifiCallingSettingsForSub.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java index 46be70dfddb..a786fa58255 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java @@ -58,9 +58,11 @@ import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.network.ims.WifiCallingQueryImsState; +import com.android.settings.network.telephony.AbstractSubscriptionPreferenceController; import com.android.settings.network.telephony.wificalling.IWifiCallingRepository; import com.android.settings.network.telephony.wificalling.WifiCallingRepository; import com.android.settings.widget.SettingsMainSwitchPreference; +import com.android.settingslib.core.AbstractPreferenceController; import kotlin.Unit; @@ -293,6 +295,11 @@ public class WifiCallingSettingsForSub extends DashboardFragment updateDescriptionForOptions( List.of(mButtonWfcMode, mButtonWfcRoamingMode, mUpdateAddress)); + + List subscriptionPreferenceControllers = + useGroup(AbstractSubscriptionPreferenceController.class); + subscriptionPreferenceControllers.forEach( + controller -> ((AbstractSubscriptionPreferenceController) controller).init(mSubId)); } @Override From 90ee129baf2056cce5841d1fcca2d19eef8d903d Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Tue, 31 Dec 2024 10:38:30 +0800 Subject: [PATCH 4/5] [Catalyst] Use range int type for BrightnessLevelPreference NO_IFTTT=Catalyst only Bug: 375895862 Flag: com.android.settings.flags.catalyst_display_settings_screen Test: devtool Change-Id: I1f5becd58e63183a56360b355d8bf753de81242f --- .../display/BrightnessLevelPreference.kt | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/com/android/settings/display/BrightnessLevelPreference.kt b/src/com/android/settings/display/BrightnessLevelPreference.kt index 9dc68db7834..f87edcbe758 100644 --- a/src/com/android/settings/display/BrightnessLevelPreference.kt +++ b/src/com/android/settings/display/BrightnessLevelPreference.kt @@ -42,19 +42,22 @@ import com.android.settingslib.datastore.SettingsSystemStore import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN import com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat -import com.android.settingslib.metadata.FloatPersistentPreference +import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceSummaryProvider +import com.android.settingslib.metadata.RangeValue import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.preference.PreferenceBinding import com.android.settingslib.transition.SettingsTransitionHelper +import java.math.BigDecimal import java.text.NumberFormat // LINT.IfChange class BrightnessLevelPreference : PreferenceMetadata, - FloatPersistentPreference, + PersistentPreference, + RangeValue, PreferenceBinding, PreferenceRestrictionMixin, PreferenceSummaryProvider, @@ -70,7 +73,7 @@ class BrightnessLevelPreference : get() = R.string.keywords_display_brightness_level override fun getSummary(context: Context): CharSequence? = - NumberFormat.getPercentInstance().format(context.brightness) + NumberFormat.getPercentInstance().format(context.brightnessPercent) override fun isEnabled(context: Context) = super.isEnabled(context) @@ -105,7 +108,7 @@ class BrightnessLevelPreference : override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW - override fun getWritePermit(context: Context, value: Float?, callingPid: Int, callingUid: Int) = + override fun getWritePermit(context: Context, value: Int?, callingPid: Int, callingUid: Int) = ReadWritePermit.DISALLOW override val sensitivityLevel @@ -113,6 +116,10 @@ class BrightnessLevelPreference : override fun storage(context: Context): KeyValueStore = BrightnessStorage(context) + override fun getMinValue(context: Context) = 0 + + override fun getMaxValue(context: Context) = 100 + private class BrightnessStorage(private val context: Context) : AbstractKeyedDataObservable(), KeyValueStore, @@ -123,7 +130,9 @@ class BrightnessLevelPreference : @Suppress("UNCHECKED_CAST") override fun getValue(key: String, valueType: Class) = - context.brightness.toFloat() as T + BigDecimal(context.brightnessPercent * 100) + .setScale(0, NumberFormat.getPercentInstance().roundingMode) + .toInt() as T override fun setValue(key: String, valueType: Class, value: T?) {} @@ -134,7 +143,7 @@ class BrightnessLevelPreference : context.displayManager.registerDisplayListener( this, HandlerExecutor.main, - /* eventFlags= */ 0, + /* eventFilter= */ 0, DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS, ) } @@ -176,21 +185,20 @@ class BrightnessLevelPreference : private val Context.displayManager: DisplayManager get() = getSystemService(DisplayManager::class.java)!! - private val Context.brightness: Double + private val Context.brightnessPercent: Double get() { - val info: BrightnessInfo? = display.brightnessInfo - val value = - info?.run { - convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum) - } - return getPercentage(value?.toDouble() ?: 0.0) + val info: BrightnessInfo = display.brightnessInfo ?: return 0.0 + return info.brightnessInGammaSpace.toPercentage() } - private fun getPercentage(value: Double): Double = + private val BrightnessInfo.brightnessInGammaSpace: Int + get() = convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum) + + private fun Int.toPercentage(): Double = when { - value > GAMMA_SPACE_MAX -> 1.0 - value < GAMMA_SPACE_MIN -> 0.0 - else -> (value - GAMMA_SPACE_MIN) / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN) + this > GAMMA_SPACE_MAX -> 1.0 + this < GAMMA_SPACE_MIN -> 0.0 + else -> (this - GAMMA_SPACE_MIN).toDouble() / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN) } } } From 0f76903817c5b92f2417e08e06651429b52b7b43 Mon Sep 17 00:00:00 2001 From: Zoey Chen Date: Fri, 20 Dec 2024 07:27:29 +0000 Subject: [PATCH 5/5] [Settings] Add search icon in region picker Bug: 38526548 Flag: EXEMPT refactor Change-Id: I1016f5f812d8f0c43a2b3a899bd70b6672f44481 --- res/values/strings.xml | 13 +- res/xml/system_language_picker.xml | 6 + .../LocaleListSearchCallback.java | 4 +- ...alePickerBaseListPreferenceController.java | 58 +++-- ...egionAndNumberingSystemPickerFragment.java | 201 +++++++++++++++++- .../SystemLocalePickerFragment.java | 5 +- 6 files changed, 257 insertions(+), 30 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 54b6fbc0da2..fe05a9861c8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -424,6 +424,9 @@ All languages + + More languages + All regions @@ -480,12 +483,20 @@ Search + + Language & region Add a language + + Choose a region Region preference - Type language name + Search languages + + Search regions + + The region you choose affects how your phone displays time, dates, temperature, and more More language settings diff --git a/res/xml/system_language_picker.xml b/res/xml/system_language_picker.xml index cccf56ea063..52623928eb4 100644 --- a/res/xml/system_language_picker.xml +++ b/res/xml/system_language_picker.xml @@ -16,9 +16,15 @@ + + diff --git a/src/com/android/settings/localepicker/LocaleListSearchCallback.java b/src/com/android/settings/localepicker/LocaleListSearchCallback.java index e24e9bd3fa1..8c6cac76262 100644 --- a/src/com/android/settings/localepicker/LocaleListSearchCallback.java +++ b/src/com/android/settings/localepicker/LocaleListSearchCallback.java @@ -16,6 +16,7 @@ package com.android.settings.localepicker; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.app.LocaleStore; @@ -25,5 +26,6 @@ import java.util.List; public interface LocaleListSearchCallback { /** Callback method for searching changes. */ - void onSearchListChanged(@NonNull List localeInfoList); + void onSearchListChanged(@NonNull List localeInfoList, + @Nullable CharSequence prefix); } diff --git a/src/com/android/settings/localepicker/LocalePickerBaseListPreferenceController.java b/src/com/android/settings/localepicker/LocalePickerBaseListPreferenceController.java index 4b0e9fa4fd4..471ca35e7e1 100644 --- a/src/com/android/settings/localepicker/LocalePickerBaseListPreferenceController.java +++ b/src/com/android/settings/localepicker/LocalePickerBaseListPreferenceController.java @@ -60,7 +60,9 @@ public abstract class LocalePickerBaseListPreferenceController extends private Map 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) { @@ -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(); } @@ -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()); @@ -112,13 +111,17 @@ public abstract class LocalePickerBaseListPreferenceController extends } @Override - public void onSearchListChanged(@NonNull List newList) { + public void onSearchListChanged(@NonNull List newList, + @Nullable CharSequence prefix) { mPreferenceCategory.removeAll(); mPreferences.clear(); final Map existingPreferences = mPreferences; - if (getPreferenceCategoryKey().contains(KEY_SUGGESTED)) { - newList = getSortedSuggestedLocaleFromSearchList( - newList, getSuggestedLocaleList()); + + List sortedList = + mIsSuggestedCategory ? getSuggestedLocaleList() : getSupportedLocaleList(); + newList = getSortedSuggestedLocaleFromSearchList(newList, sortedList); + if (mIsSuggestedCategory && getParentLocale() != null) { + newList = getSortedSuggestedRegionFromSearchList(prefix, newList, sortedList); } setupPreference(newList, existingPreferences); } @@ -138,6 +141,23 @@ public abstract class LocalePickerBaseListPreferenceController extends return searchItem; } + private List getSortedSuggestedRegionFromSearchList( + @Nullable CharSequence prefix, + List listOptions, + List listSuggested) { + List 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 localeInfoList, Map existingPreferences) { Log.d(TAG, "setupPreference: isNumberingMode = " + isNumberingMode()); @@ -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 getSuggestedLocaleList() { - mLocaleOptions.clear(); + setupLocaleList(); if (mLocaleList != null && !mLocaleList.isEmpty()) { mLocaleOptions.addAll(mLocaleList.stream() .filter(localeInfo -> localeInfo.isSuggested()) @@ -199,6 +221,7 @@ public abstract class LocalePickerBaseListPreferenceController extends } protected List getSupportedLocaleList() { + setupLocaleList(); if (mLocaleList != null && !mLocaleList.isEmpty()) { mLocaleOptions.addAll(mLocaleList.stream() .filter(localeInfo -> !localeInfo.isSuggested()) @@ -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 getSortedLocaleList( List localeInfos) { final Locale sortingLocale = Locale.getDefault(); diff --git a/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java b/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java index 83c87b03499..293c1ee0a6a 100644 --- a/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java +++ b/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java @@ -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. @@ -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.

*/ -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"; @@ -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 mLocaleOptions; + @SuppressWarnings("NullAway") + private List 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) { @@ -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); } - mActivity.setTitle(mLocaleInfo.getFullNameNative()); + Log.d(TAG, "onCreate, mIsNumberingMode = " + mIsNumberingMode); + if (!mIsNumberingMode) { + mActivity.setTitle(R.string.region_selection_title); + } + + TopIntroPreference topIntroPreference = findPreference(KEY_TOP_INTRO_PREFERENCE); + if (topIntroPreference != null) { + topIntroPreference.setVisible(!mIsNumberingMode); + } + + if (mSystemLocaleAllListPreferenceController != null) { + mOriginalLocaleInfos = + mSystemLocaleAllListPreferenceController.getSupportedLocaleList(); + } } @Override @@ -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 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 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) 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; diff --git a/src/com/android/settings/localepicker/SystemLocalePickerFragment.java b/src/com/android/settings/localepicker/SystemLocalePickerFragment.java index 5a007318a55..183990bfb75 100644 --- a/src/com/android/settings/localepicker/SystemLocalePickerFragment.java +++ b/src/com/android/settings/localepicker/SystemLocalePickerFragment.java @@ -222,8 +222,9 @@ public class SystemLocalePickerFragment extends DashboardFragment implements if (mRecyclerView != null) { mRecyclerView.post(() -> mRecyclerView.scrollToPosition(0)); } - mSystemLocaleAllListPreferenceController.onSearchListChanged(mLocaleOptions); - mSuggestedListPreferenceController.onSearchListChanged(mLocaleOptions); + + mSystemLocaleAllListPreferenceController.onSearchListChanged(mLocaleOptions, null); + mSuggestedListPreferenceController.onSearchListChanged(mLocaleOptions, null); } // TODO: decide if this is enough, or we want to use a BreakIterator...