From 47a83c7ed72e62f8e5a62f59b2c88118c1d8eb78 Mon Sep 17 00:00:00 2001 From: Quang Anh Luong Date: Thu, 18 Jul 2024 14:25:15 +0900 Subject: [PATCH 01/10] Fix mac randomization value mismatch WifiConfigController2 passes in WifiConfiguration.MacRandomizationSetting to methods that expect WifiEntry.Privacy enums, resulting in the wrong values being saved and displayed for the edit dialog. Fix this by making sure the correct WifiEntry values are used. Note that the Network Details Page uses WifiPrivacyPreferenceController2, which uses the correct values. Flag: EXEMPT bugfix Bug: 284230986 Test: atest WifiPrivacyPreferenceController2Test, atest WifiConfigController2Test, manually verify adding a new network saves the config with RANDOMIZATION_AUTO by default and changing the value works correctly. Change-Id: I84dc7cc50d04360aca611ca80ee400a97b693722 --- .../settings/wifi/WifiConfigController2.java | 4 +-- .../WifiPrivacyPreferenceController2.java | 19 +++++++------- .../wifi/WifiConfigController2Test.java | 25 +++++++++---------- .../WifiPrivacyPreferenceController2Test.kt | 4 +-- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java index 70e08eb9033..1ea0103c28e 100644 --- a/src/com/android/settings/wifi/WifiConfigController2.java +++ b/src/com/android/settings/wifi/WifiConfigController2.java @@ -344,7 +344,7 @@ public class WifiConfigController2 implements TextWatcher, if (mPrivacySettingsSpinner != null) { final int prefMacValue = WifiPrivacyPreferenceController2 - .translateMacRandomizedValueToPrefValue(config.macRandomizationSetting); + .translateWifiEntryPrivacyToPrefValue(mWifiEntry.getPrivacy()); mPrivacySettingsSpinner.setSelection(prefMacValue); } @@ -863,7 +863,7 @@ public class WifiConfigController2 implements TextWatcher, if (mPrivacySettingsSpinner != null) { config.macRandomizationSetting = WifiPrivacyPreferenceController2 - .translatePrefValueToMacRandomizedValue(mPrivacySettingsSpinner + .translatePrefValueToWifiConfigSetting(mPrivacySettingsSpinner .getSelectedItemPosition()); } diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java index 5d393e54a21..0c67c04622e 100644 --- a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java +++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java @@ -17,6 +17,7 @@ package com.android.settings.wifi.details2; import android.content.Context; +import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import androidx.annotation.NonNull; @@ -98,30 +99,30 @@ public class WifiPrivacyPreferenceController2 extends BasePreferenceController i private static final int PREF_RANDOMIZATION_NONE = 1; /** - * Returns preference index value. + * Translates a WifiEntry.Privacy value to the matching preference index value. * - * @param macRandomized is mac randomized value + * @param privacy WifiEntry.Privacy value * @return index value of preference */ - public static int translateMacRandomizedValueToPrefValue(int macRandomized) { - return (macRandomized == WifiEntry.PRIVACY_RANDOMIZED_MAC) + public static int translateWifiEntryPrivacyToPrefValue(@WifiEntry.Privacy int privacy) { + return (privacy == WifiEntry.PRIVACY_RANDOMIZED_MAC) ? PREF_RANDOMIZATION_PERSISTENT : PREF_RANDOMIZATION_NONE; } /** - * Returns mac randomized value. + * Translates the pref value to WifiConfiguration.MacRandomizationSetting value * * @param prefMacRandomized is preference index value - * @return mac randomized value + * @return WifiConfiguration.MacRandomizationSetting value */ - public static int translatePrefValueToMacRandomizedValue(int prefMacRandomized) { + public static int translatePrefValueToWifiConfigSetting(int prefMacRandomized) { return (prefMacRandomized == PREF_RANDOMIZATION_PERSISTENT) - ? WifiEntry.PRIVACY_RANDOMIZED_MAC : WifiEntry.PRIVACY_DEVICE_MAC; + ? WifiConfiguration.RANDOMIZATION_AUTO : WifiConfiguration.RANDOMIZATION_NONE; } private void updateSummary(ListPreference preference, int macRandomized) { // Translates value here to set RANDOMIZATION_PERSISTENT as first item in UI for better UX. - final int prefMacRandomized = translateMacRandomizedValueToPrefValue(macRandomized); + final int prefMacRandomized = translateWifiEntryPrivacyToPrefValue(macRandomized); preference.setSummary(preference.getEntries()[prefMacRandomized]); } } diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java index 7d96496282e..d985ee5b9d3 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java @@ -459,46 +459,45 @@ public class WifiConfigController2Test { public void loadMacRandomizedValue_shouldPersistentAsDefault() { final Spinner privacySetting = mView.findViewById(R.id.privacy_settings); final int prefPersist = - WifiPrivacyPreferenceController2.translateMacRandomizedValueToPrefValue( - WifiConfiguration.RANDOMIZATION_PERSISTENT); + WifiPrivacyPreferenceController2.translateWifiEntryPrivacyToPrefValue( + WifiEntry.PRIVACY_RANDOMIZED_MAC); assertThat(privacySetting.getVisibility()).isEqualTo(View.VISIBLE); assertThat(privacySetting.getSelectedItemPosition()).isEqualTo(prefPersist); } @Test - public void loadSavedMacRandomizedPersistentValue_shouldCorrectMacValue() { - checkSavedMacRandomizedValue(WifiConfiguration.RANDOMIZATION_PERSISTENT); + public void loadSavedPrivacyRandomizedMacValue_shouldCorrectMacValue() { + checkSavedMacRandomizedValue(WifiEntry.PRIVACY_RANDOMIZED_MAC); } @Test - public void loadSavedMacRandomizedNoneValue_shouldCorrectMacValue() { - checkSavedMacRandomizedValue(WifiConfiguration.RANDOMIZATION_NONE); + public void loadSavedPrivacyDeviceMacValue_shouldCorrectMacValue() { + checkSavedMacRandomizedValue(WifiEntry.PRIVACY_DEVICE_MAC); } - private void checkSavedMacRandomizedValue(int macRandomizedValue) { + private void checkSavedMacRandomizedValue(@WifiEntry.Privacy int privacy) { when(mWifiEntry.isSaved()).thenReturn(true); final WifiConfiguration mockWifiConfig = spy(new WifiConfiguration()); when(mockWifiConfig.getIpConfiguration()).thenReturn(mock(IpConfiguration.class)); when(mWifiEntry.getWifiConfiguration()).thenReturn(mockWifiConfig); - mockWifiConfig.macRandomizationSetting = macRandomizedValue; + when(mWifiEntry.getPrivacy()).thenReturn(privacy); createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false); final Spinner privacySetting = mView.findViewById(R.id.privacy_settings); final int expectedPrefValue = - WifiPrivacyPreferenceController2.translateMacRandomizedValueToPrefValue( - macRandomizedValue); + WifiPrivacyPreferenceController2.translateWifiEntryPrivacyToPrefValue(privacy); assertThat(privacySetting.getVisibility()).isEqualTo(View.VISIBLE); assertThat(privacySetting.getSelectedItemPosition()).isEqualTo(expectedPrefValue); } @Test - public void saveMacRandomizedValue_noChanged_shouldPersistentAsDefault() { + public void saveMacRandomizedValue_noChanged_shouldAutoAsDefault() { createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false); WifiConfiguration config = mController.getConfig(); assertThat(config.macRandomizationSetting).isEqualTo( - WifiConfiguration.RANDOMIZATION_PERSISTENT); + WifiConfiguration.RANDOMIZATION_AUTO); } @Test @@ -506,7 +505,7 @@ public class WifiConfigController2Test { createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false); final Spinner privacySetting = mView.findViewById(R.id.privacy_settings); final int prefMacNone = - WifiPrivacyPreferenceController2.translateMacRandomizedValueToPrefValue( + WifiPrivacyPreferenceController2.translateWifiEntryPrivacyToPrefValue( WifiConfiguration.RANDOMIZATION_NONE); privacySetting.setSelection(prefMacNone); diff --git a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt index cb1f997f4e5..9260409af37 100644 --- a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt +++ b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt @@ -59,7 +59,7 @@ class WifiPrivacyPreferenceController2Test { controller.updateState(preference) - val prefValue = WifiPrivacyPreferenceController2.translateMacRandomizedValueToPrefValue( + val prefValue = WifiPrivacyPreferenceController2.translateWifiEntryPrivacyToPrefValue( WifiEntry.PRIVACY_DEVICE_MAC ) assertThat(preference.entry).isEqualTo(preferenceStrings[prefValue]) @@ -73,7 +73,7 @@ class WifiPrivacyPreferenceController2Test { controller.updateState(preference) - val prefValue = WifiPrivacyPreferenceController2.translateMacRandomizedValueToPrefValue( + val prefValue = WifiPrivacyPreferenceController2.translateWifiEntryPrivacyToPrefValue( WifiEntry.PRIVACY_RANDOMIZED_MAC ) assertThat(preference.entry).isEqualTo(preferenceStrings[prefValue]) From df9b9abd4dadf926c1ef6a142ad78495ee40a8b4 Mon Sep 17 00:00:00 2001 From: Oleg Blinnikov Date: Mon, 22 Jul 2024 15:35:43 +0000 Subject: [PATCH 02/10] External display page is not searchable Page was moved to connnected devices from connected devices->connection pref. This makes the page non-searchable, therefore must be dropped from the search index. Change-Id: I437b1310bedc5943041eb0baa73b2e3152eb9ebd Bug: 294015706 Test: manual Flag: EXEMPT bugfix --- res/values/strings.xml | 2 -- res/xml/external_display_settings.xml | 1 - .../display/ExternalDisplayPreferenceFragment.java | 13 +++---------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index fd02785108d..d895e88939c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1896,8 +1896,6 @@ Restart - - mirror, external display, connected display, usb display, resolution, rotation On diff --git a/res/xml/external_display_settings.xml b/res/xml/external_display_settings.xml index 00472115e0c..e4dc5bbc12b 100644 --- a/res/xml/external_display_settings.xml +++ b/res/xml/external_display_settings.xml @@ -17,6 +17,5 @@ diff --git a/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java b/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java index 09f8e92ea49..7b5bef625f6 100644 --- a/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java +++ b/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java @@ -48,9 +48,6 @@ import com.android.settings.SettingsPreferenceFragmentBase; import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.DisplayListener; import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.Injector; import com.android.settings.core.SubSettingLauncher; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settingslib.search.Indexable; -import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.IllustrationPreference; import com.android.settingslib.widget.MainSwitchPreference; @@ -63,12 +60,8 @@ import java.util.List; /** * The Settings screen for External Displays configuration and connection management. */ -@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) -public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmentBase - implements Indexable { +public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmentBase { static final int EXTERNAL_DISPLAY_SETTINGS_RESOURCE = R.xml.external_display_settings; - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider(EXTERNAL_DISPLAY_SETTINGS_RESOURCE); static final String DISPLAYS_LIST_PREFERENCE_KEY = "displays_list_preference"; static final String EXTERNAL_DISPLAY_USE_PREFERENCE_KEY = "external_display_use_preference"; static final String EXTERNAL_DISPLAY_ROTATION_KEY = "external_display_rotation"; @@ -77,7 +70,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen R.string.external_display_change_resolution_footer_title; static final int EXTERNAL_DISPLAY_LANDSCAPE_DRAWABLE = R.drawable.external_display_mirror_landscape; - static final int EXTERANAL_DISPLAY_TITLE_RESOURCE = + static final int EXTERNAL_DISPLAY_TITLE_RESOURCE = R.string.external_display_settings_title; static final int EXTERNAL_DISPLAY_USE_TITLE_RESOURCE = R.string.external_display_use_title; @@ -332,7 +325,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen return; } } - activity.setTitle(EXTERANAL_DISPLAY_TITLE_RESOURCE); + activity.setTitle(EXTERNAL_DISPLAY_TITLE_RESOURCE); } private void showTextWhenNoDisplaysToShow(@NonNull final PreferenceScreen screen, From ec6e93f3f46e66d6ecfa8ed58b644b8ccf9e156d Mon Sep 17 00:00:00 2001 From: chayemme Date: Sun, 14 Jul 2024 20:50:56 +0530 Subject: [PATCH 03/10] Developer option crashed when Bluetooth feature disabled When Bluetooth feature is disabled, crash observed with developer option is selected Bug: 352864331 Change-Id: If4239f710847f2e01a3b419e1ce6f783f0522219 --- ...etoothMaxConnectedAudioDevicesPreferenceController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java index a1467c80ac5..467a8cac6bd 100644 --- a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java +++ b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java @@ -45,9 +45,10 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceController extends super(context); final BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class); - - mDefaultMaxConnectedAudioDevices = - bluetoothManager.getAdapter().getMaxConnectedAudioDevices(); + if(bluetoothManager != null && bluetoothManager.getAdapter() != null) { + mDefaultMaxConnectedAudioDevices = + bluetoothManager.getAdapter().getMaxConnectedAudioDevices(); + } } @Override From 1821d25d39b683efd8e951ff62058908f1bc355e Mon Sep 17 00:00:00 2001 From: Oleg Blinnikov Date: Mon, 22 Jul 2024 14:17:17 +0000 Subject: [PATCH 04/10] External display mode limit flag in settings Use external display mode limit flag and override for the flag in the settings to allow selection of any resolutions with the refresh rate around 60 Change-Id: I180ba97eb9dfa14a386a7517f29504d369268563 Bug: 351831642 Flag: com.android.settings.flags.resolution_and_enable_connected_display_setting Test: atest ResolutionPreferenceFragmentTest --- .../ExternalDisplaySettingsConfiguration.java | 9 +++ .../display/ResolutionPreferenceFragment.java | 21 ++++++- .../display/ExternalDisplayTestBase.java | 1 + .../ResolutionPreferenceFragmentTest.java | 63 ++++++++++++++++--- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/com/android/settings/connecteddevice/display/ExternalDisplaySettingsConfiguration.java b/src/com/android/settings/connecteddevice/display/ExternalDisplaySettingsConfiguration.java index 89d464c9a4e..c9ea8ae0a04 100644 --- a/src/com/android/settings/connecteddevice/display/ExternalDisplaySettingsConfiguration.java +++ b/src/com/android/settings/connecteddevice/display/ExternalDisplaySettingsConfiguration.java @@ -23,6 +23,8 @@ import static android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CONNECT import static android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.display.feature.flags.Flags.enableModeLimitForExternalDisplay; + import android.content.Context; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; @@ -271,6 +273,13 @@ public class ExternalDisplaySettingsConfiguration { public void setUserPreferredDisplayMode(int displayId, @NonNull Mode mode) { DisplayManagerGlobal.getInstance().setUserPreferredDisplayMode(displayId, mode); } + + /** + * @return true if the display mode limit flag enabled. + */ + public boolean isModeLimitForExternalDisplayEnabled() { + return enableModeLimitForExternalDisplay(); + } } public abstract static class DisplayListener implements DisplayManager.DisplayListener { diff --git a/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragment.java b/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragment.java index 10314cb1e21..db81be89a42 100644 --- a/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragment.java +++ b/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragment.java @@ -52,7 +52,7 @@ import java.util.ArrayList; import java.util.HashSet; public class ResolutionPreferenceFragment extends SettingsPreferenceFragmentBase { - private static final String TAG = "ResolutionPreferenceFragment"; + private static final String TAG = "ResolutionPreference"; static final int DEFAULT_LOW_REFRESH_RATE = 60; static final String MORE_OPTIONS_KEY = "more_options"; static final String TOP_OPTIONS_KEY = "top_options"; @@ -60,6 +60,8 @@ public class ResolutionPreferenceFragment extends SettingsPreferenceFragmentBase R.string.external_display_more_options_title; static final int EXTERNAL_DISPLAY_RESOLUTION_SETTINGS_RESOURCE = R.xml.external_display_resolution_settings; + static final String DISPLAY_MODE_LIMIT_OVERRIDE_PROP = "persist.sys.com.android.server.display" + + ".feature.flags.enable_mode_limit_for_external_display-override"; @Nullable private Injector mInjector; @Nullable @@ -323,16 +325,29 @@ public class ResolutionPreferenceFragment extends SettingsPreferenceFragmentBase } } + private boolean isDisplayResolutionLimitEnabled() { + if (mInjector == null) { + return false; + } + var flagOverride = mInjector.getSystemProperty(DISPLAY_MODE_LIMIT_OVERRIDE_PROP); + var isOverrideEnabled = "true".equals(flagOverride); + var isOverrideEnabledOrNotSet = !"false".equals(flagOverride); + return (mInjector.isModeLimitForExternalDisplayEnabled() && isOverrideEnabledOrNotSet) + || isOverrideEnabled; + } + private void updateDisplayModeLimits(@Nullable Context context) { if (context == null) { return; } mExternalDisplayPeakRefreshRate = getResources(context).getInteger( com.android.internal.R.integer.config_externalDisplayPeakRefreshRate); - mExternalDisplayPeakWidth = getResources(context).getInteger( + if (isDisplayResolutionLimitEnabled()) { + mExternalDisplayPeakWidth = getResources(context).getInteger( com.android.internal.R.integer.config_externalDisplayPeakWidth); - mExternalDisplayPeakHeight = getResources(context).getInteger( + mExternalDisplayPeakHeight = getResources(context).getInteger( com.android.internal.R.integer.config_externalDisplayPeakHeight); + } mRefreshRateSynchronizationEnabled = getResources(context).getBoolean( com.android.internal.R.bool.config_refreshRateSynchronizationEnabled); Log.d(TAG, "mExternalDisplayPeakRefreshRate=" + mExternalDisplayPeakRefreshRate); diff --git a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayTestBase.java b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayTestBase.java index 60b034288a0..4cba1ef4666 100644 --- a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayTestBase.java +++ b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayTestBase.java @@ -87,6 +87,7 @@ public class ExternalDisplayTestBase { doReturn(mHandler).when(mMockedInjector).getHandler(); doReturn("").when(mMockedInjector).getSystemProperty( VIRTUAL_DISPLAY_PACKAGE_NAME_SYSTEM_PROPERTY); + doReturn(true).when(mMockedInjector).isModeLimitForExternalDisplayEnabled(); doAnswer((arg) -> { mListener = arg.getArgument(0); return null; diff --git a/tests/unit/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragmentTest.java b/tests/unit/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragmentTest.java index ee38a1cbae2..c8663622e74 100644 --- a/tests/unit/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragmentTest.java +++ b/tests/unit/src/com/android/settings/connecteddevice/display/ResolutionPreferenceFragmentTest.java @@ -17,6 +17,7 @@ package com.android.settings.connecteddevice.display; import static android.view.Display.INVALID_DISPLAY; +import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.DISPLAY_MODE_LIMIT_OVERRIDE_PROP; import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.EXTERNAL_DISPLAY_RESOLUTION_SETTINGS_RESOURCE; import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.MORE_OPTIONS_KEY; import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.TOP_OPTIONS_KEY; @@ -29,6 +30,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.res.Resources; +import android.util.Pair; import android.view.View; import android.widget.TextView; @@ -83,18 +85,52 @@ public class ResolutionPreferenceFragmentTest extends ExternalDisplayTestBase { @Test @UiThreadTest - public void testModePreferences() { - mDisplayIdArg = 1; - initFragment(); - mHandler.flush(); - PreferenceCategory topPref = mPreferenceScreen.findPreference(TOP_OPTIONS_KEY); - assertThat(topPref).isNotNull(); - PreferenceCategory morePref = mPreferenceScreen.findPreference(MORE_OPTIONS_KEY); - assertThat(morePref).isNotNull(); + public void testModePreferences_modeLimitFlagIsOn_noOverride() { + doReturn(true).when(mMockedInjector).isModeLimitForExternalDisplayEnabled(); + doReturn(null).when(mMockedInjector).getSystemProperty( + DISPLAY_MODE_LIMIT_OVERRIDE_PROP); + var topAndMorePref = runTestModePreferences(); + PreferenceCategory topPref = topAndMorePref.first, morePref = topAndMorePref.second; assertThat(topPref.getPreferenceCount()).isEqualTo(3); assertThat(morePref.getPreferenceCount()).isEqualTo(1); } + @Test + @UiThreadTest + public void testModePreferences_noModeLimitFlag_overrideIsTrue() { + doReturn(false).when(mMockedInjector).isModeLimitForExternalDisplayEnabled(); + doReturn("true").when(mMockedInjector).getSystemProperty( + DISPLAY_MODE_LIMIT_OVERRIDE_PROP); + var topAndMorePref = runTestModePreferences(); + PreferenceCategory topPref = topAndMorePref.first, morePref = topAndMorePref.second; + assertThat(topPref.getPreferenceCount()).isEqualTo(3); + assertThat(morePref.getPreferenceCount()).isEqualTo(1); + } + + @Test + @UiThreadTest + public void testModePreferences_noModeLimitFlag_noOverride() { + doReturn(false).when(mMockedInjector).isModeLimitForExternalDisplayEnabled(); + doReturn(null).when(mMockedInjector).getSystemProperty( + DISPLAY_MODE_LIMIT_OVERRIDE_PROP); + var topAndMorePref = runTestModePreferences(); + PreferenceCategory topPref = topAndMorePref.first, morePref = topAndMorePref.second; + assertThat(topPref.getPreferenceCount()).isEqualTo(3); + assertThat(morePref.getPreferenceCount()).isEqualTo(2); + } + + @Test + @UiThreadTest + public void testModePreferences_modeLimitFlagIsOn_butOverrideIsFalse() { + doReturn(true).when(mMockedInjector).isModeLimitForExternalDisplayEnabled(); + doReturn("false").when(mMockedInjector).getSystemProperty( + DISPLAY_MODE_LIMIT_OVERRIDE_PROP); + var topAndMorePref = runTestModePreferences(); + PreferenceCategory topPref = topAndMorePref.first, morePref = topAndMorePref.second; + assertThat(topPref.getPreferenceCount()).isEqualTo(3); + assertThat(morePref.getPreferenceCount()).isEqualTo(2); + } + @Test @UiThreadTest public void testModeChange() { @@ -109,6 +145,17 @@ public class ResolutionPreferenceFragmentTest extends ExternalDisplayTestBase { verify(mMockedInjector).setUserPreferredDisplayMode(mDisplayIdArg, mode); } + private Pair runTestModePreferences() { + mDisplayIdArg = 1; + initFragment(); + mHandler.flush(); + PreferenceCategory topPref = mPreferenceScreen.findPreference(TOP_OPTIONS_KEY); + assertThat(topPref).isNotNull(); + PreferenceCategory morePref = mPreferenceScreen.findPreference(MORE_OPTIONS_KEY); + assertThat(morePref).isNotNull(); + return new Pair<>(topPref, morePref); + } + private void initFragment() { if (mFragment != null) { return; From 615133cc00c1e7f05e9002fb59396fd1e5785183 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Wed, 24 Jul 2024 07:55:12 +0000 Subject: [PATCH 05/10] Fix the scrolling behavior in Settings homepage - we always scroll to top of homepage first. - if target position already exist on screen, we don't need to scroll. - if target position doesn't exist on screen, scroll to the target position. Bug: 349696107 Test: manual Flag: com.android.settings.flags.homepage_revamp Change-Id: I18c314d19eb38f22f799cb8bf087bf71f4f7d466 --- .../widget/HighlightableTopLevelPreferenceAdapter.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java index 9ddec5cb0c5..15e83de9149 100644 --- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java +++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java @@ -17,6 +17,7 @@ package com.android.settings.widget; import android.content.Context; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.Log; @@ -212,6 +213,14 @@ public class HighlightableTopLevelPreferenceAdapter extends RoundCornerPreferenc // Scroll to the top to reset the position. mRecyclerView.nestedScrollBy(0, -mRecyclerView.getHeight()); + // get the visible area of the recycler view + Rect rvRect = new Rect(); + mRecyclerView.getGlobalVisibleRect(rvRect); + if (Flags.homepageRevamp() && view.getBottom() <= rvRect.height()) { + // the request position already fully visible in the visible area + return; + } + final int scrollY = view.getTop(); if (scrollY > 0) { mRecyclerView.nestedScrollBy(0, scrollY); From 38aaa276251d8265d31cdf25095da12040b1b684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Wed, 24 Jul 2024 17:53:42 +0200 Subject: [PATCH 06/10] Disable all preferences (except toggle) when a mode is disabled Also set some alpha on the circles so that they look more or less disabled as well. Fixes: 354867828 Test: atest com.android.settings.notification.modes Flag: android.app.modes_ui Change-Id: I53ef5e381d37afa20b0532f3c7ddb3f106b2e85e --- .../modes/CircularIconsPreference.java | 31 +++++++++++++++ ...nterruptionFilterPreferenceController.java | 1 + .../ZenModeAppsLinkPreferenceController.java | 11 +++--- ...enModeDisplayLinkPreferenceController.java | 10 ++--- .../notification/modes/ZenModeFragment.java | 3 ++ .../ZenModeOtherLinkPreferenceController.java | 1 + ...ZenModePeopleLinkPreferenceController.java | 1 + .../ZenModePreferenceCategoryController.java | 39 +++++++++++++++++++ .../modes/CircularIconsPreferenceTest.java | 28 +++++++++++++ ...ruptionFilterPreferenceControllerTest.java | 12 ++++++ ...nModeAppsLinkPreferenceControllerTest.java | 11 ++++++ ...deDisplayLinkPreferenceControllerTest.java | 13 +++++++ ...ModeOtherLinkPreferenceControllerTest.java | 12 ++++++ ...odePeopleLinkPreferenceControllerTest.java | 11 ++++++ 14 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 src/com/android/settings/notification/modes/ZenModePreferenceCategoryController.java diff --git a/src/com/android/settings/notification/modes/CircularIconsPreference.java b/src/com/android/settings/notification/modes/CircularIconsPreference.java index 1ce34766fdc..e3cd94848b0 100644 --- a/src/com/android/settings/notification/modes/CircularIconsPreference.java +++ b/src/com/android/settings/notification/modes/CircularIconsPreference.java @@ -51,6 +51,8 @@ import java.util.concurrent.Executor; public class CircularIconsPreference extends RestrictedPreference { + private static final float DISABLED_ITEM_ALPHA = 0.3f; + private Executor mUiExecutor; @Nullable private LinearLayout mIconContainer; @@ -98,6 +100,14 @@ public class CircularIconsPreference extends RestrictedPreference { displayIconsIfPending(); } + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (mIconContainer != null) { + applyEnabledToIcons(mIconContainer, enabled); + } + } + private void displayIconsIfPending() { CircularIconSet pendingIconSet = mPendingDisplayIconSet; if (pendingIconSet != null) { @@ -211,6 +221,8 @@ public class CircularIconsPreference extends RestrictedPreference { textView.setText(getContext().getString(R.string.zen_mode_plus_n_items, extraItems)); } + applyEnabledToIcons(mIconContainer, isEnabled()); + // Display icons when all are ready (more consistent than randomly loading). mPendingLoadIconsFuture = Futures.allAsList(iconFutures); FutureUtil.whenDone( @@ -224,6 +236,13 @@ public class CircularIconsPreference extends RestrictedPreference { mUiExecutor); } + private void applyEnabledToIcons(ViewGroup container, boolean enabled) { + for (int i = 0; i < container.getChildCount(); i++) { + View child = container.getChildAt(i); + child.setAlpha(enabled ? 1.0f : DISABLED_ITEM_ALPHA); + } + } + private static Drawable getPlaceholderImage(Context context) { ShapeDrawable placeholder = new ShapeDrawable(new OvalShape()); placeholder.setTintList(Utils.getColorAttr(context, @@ -249,6 +268,18 @@ public class CircularIconsPreference extends RestrictedPreference { return parent.getChildAt(parent.getChildCount() - 1); } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + List getViews() { + if (mIconContainer == null) { + return List.of(); + } + ArrayList views = new ArrayList<>(); + for (int i = 0; i < mIconContainer.getChildCount(); i++) { + views.add(mIconContainer.getChildAt(i)); + } + return views; + } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) List getIcons() { if (mIconContainer == null) { diff --git a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java index 8bdeea40762..9d4a1725bcc 100644 --- a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java +++ b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java @@ -44,6 +44,7 @@ class InterruptionFilterPreferenceController extends AbstractZenModePreferenceCo @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { + preference.setEnabled(zenMode.isEnabled()); boolean filteringNotifications = zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; ((TwoStatePreference) preference).setChecked(filteringNotifications); diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java index 962e0162310..1521a8b4f09 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java @@ -34,7 +34,6 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.notification.modes.ZenMode; @@ -95,11 +94,11 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr Bundle bundle = new Bundle(); bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId()); // TODO(b/332937635): Update metrics category - preference.setIntent(new SubSettingLauncher(mContext) - .setDestination(ZenModeAppsFragment.class.getName()) - .setSourceMetricsCategory(0) - .setArguments(bundle) - .toIntent()); + preference.setIntent( + ZenSubSettingLauncher.forModeFragment(mContext, ZenModeAppsFragment.class, + zenMode.getId(), 0).toIntent()); + preference.setEnabled(zenMode.isEnabled()); + mZenMode = zenMode; mPreference = (CircularIconsPreference) preference; diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java index d3559f1cf5f..bba5e342732 100644 --- a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java @@ -24,7 +24,6 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.preference.Preference; -import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; @@ -43,11 +42,10 @@ class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceCo Bundle bundle = new Bundle(); bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId()); // TODO(b/332937635): Update metrics category - preference.setIntent(new SubSettingLauncher(mContext) - .setDestination(ZenModeDisplayFragment.class.getName()) - .setSourceMetricsCategory(0) - .setArguments(bundle) - .toIntent()); + preference.setIntent( + ZenSubSettingLauncher.forModeFragment(mContext, ZenModeDisplayFragment.class, + zenMode.getId(), 0).toIntent()); + preference.setEnabled(zenMode.isEnabled()); } @Override diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index 7d7631bef02..06612844dba 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -53,12 +53,15 @@ public class ZenModeFragment extends ZenModeFragmentBase { prefControllers.add(new ZenModeHeaderController(context, "header", this)); prefControllers.add( new ZenModeButtonPreferenceController(context, "activate", this, mBackend)); + prefControllers.add(new ZenModePreferenceCategoryController(context, "modes_filters")); prefControllers.add(new ZenModePeopleLinkPreferenceController( context, "zen_mode_people", mHelperBackend)); prefControllers.add(new ZenModeAppsLinkPreferenceController( context, "zen_mode_apps", this, mBackend, mHelperBackend)); prefControllers.add(new ZenModeOtherLinkPreferenceController( context, "zen_other_settings", mHelperBackend)); + prefControllers.add( + new ZenModePreferenceCategoryController(context, "modes_additional_actions")); prefControllers.add(new ZenModeDisplayLinkPreferenceController( context, "mode_display_settings", mBackend, mHelperBackend)); prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context, diff --git a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java index d7bd5177aaf..15e0edcf1df 100644 --- a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java @@ -70,6 +70,7 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont ZenSubSettingLauncher.forModeFragment(mContext, ZenModeOtherFragment.class, zenMode.getId(), 0).toIntent()); + preference.setEnabled(zenMode.isEnabled()); preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)); ((CircularIconsPreference) preference).displayIcons(getSoundIcons(zenMode.getPolicy())); } diff --git a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java index 762cdd57ca6..b5938c6d3ea 100644 --- a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java @@ -92,6 +92,7 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon ZenSubSettingLauncher.forModeFragment(mContext, ZenModePeopleFragment.class, zenMode.getId(), 0).toIntent()); + preference.setEnabled(zenMode.isEnabled()); preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy())); ((CircularIconsPreference) preference).displayIcons(getPeopleIcons(zenMode.getPolicy())); } diff --git a/src/com/android/settings/notification/modes/ZenModePreferenceCategoryController.java b/src/com/android/settings/notification/modes/ZenModePreferenceCategoryController.java new file mode 100644 index 00000000000..6ebcb1fd1f2 --- /dev/null +++ b/src/com/android/settings/notification/modes/ZenModePreferenceCategoryController.java @@ -0,0 +1,39 @@ +/* + * 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.notification.modes; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; + +import com.android.settingslib.notification.modes.ZenMode; + +/** + * Simple {@link AbstractZenModePreferenceController} used for all {@code PreferenceCategory} + * entries in {@link ZenModeFragment} that should be disabled when the mode is disabled. + */ +class ZenModePreferenceCategoryController extends AbstractZenModePreferenceController { + ZenModePreferenceCategoryController(@NonNull Context context, @NonNull String key) { + super(context, key); + } + + @Override + void updateState(Preference preference, @NonNull ZenMode zenMode) { + preference.setEnabled(zenMode.isEnabled()); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java index 73754df349b..ce23fc4ba05 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/CircularIconsPreferenceTest.java @@ -226,4 +226,32 @@ public class CircularIconsPreferenceTest { mPreference.displayIcons(one); mPreference.displayIcons(same); // if no exception, wasn't called. } + + @Test + public void setEnabled_afterDisplayIcons_showsEnabledOrDisabledImages() { + CircularIconSet iconSet = new CircularIconSet<>(ImmutableList.of(1, 2), + ColorDrawable::new); + bindAndMeasureViewHolder(VIEW_WIDTH); + mPreference.displayIcons(iconSet); + assertThat(mPreference.getViews()).hasSize(2); + + mPreference.setEnabled(false); + assertThat(mPreference.getViews().get(0).getAlpha()).isLessThan(1f); + + mPreference.setEnabled(true); + assertThat(mPreference.getViews().get(0).getAlpha()).isEqualTo(1f); + } + + @Test + public void setEnabled_beforeDisplayIcons_showsEnabledOrDisabledImages() { + CircularIconSet iconSet = new CircularIconSet<>(ImmutableList.of(1, 2), + ColorDrawable::new); + + mPreference.setEnabled(false); + bindAndMeasureViewHolder(VIEW_WIDTH); + mPreference.displayIcons(iconSet); + + assertThat(mPreference.getViews()).hasSize(2); + assertThat(mPreference.getViews().get(0).getAlpha()).isLessThan(1f); + } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java index 61d31929f4f..0c3f8e1815d 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java @@ -67,6 +67,18 @@ public final class InterruptionFilterPreferenceControllerTest { mController = new InterruptionFilterPreferenceController(mContext, "something", mBackend); } + @Test + public void testUpdateState_disabled() { + TwoStatePreference preference = mock(TwoStatePreference.class); + ZenMode zenMode = new TestModeBuilder() + .setEnabled(false) + .build(); + + mController.updateZenMode(preference, zenMode); + + verify(preference).setEnabled(false); + } + @Test public void testUpdateState_all() { TwoStatePreference preference = mock(TwoStatePreference.class); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java index cc4d30643f7..301ff9092e0 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java @@ -141,6 +141,17 @@ public final class ZenModeAppsLinkPreferenceControllerTest { assertThat(mController.isAvailable()).isTrue(); } + @Test + public void testUpdateState_disabled() { + ZenMode zenMode = new TestModeBuilder() + .setEnabled(false) + .build(); + + mController.updateState(mPreference, zenMode); + + assertThat(mPreference.isEnabled()).isFalse(); + } + @Test public void testUpdateSetsIntent() { // Create a zen mode that allows priority channels to breakthrough. diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java index 6c3d74fed8e..29350f6737f 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java @@ -28,6 +28,7 @@ import android.platform.test.flag.junit.SetFlagsRule; import androidx.preference.Preference; import com.android.settingslib.notification.modes.TestModeBuilder; +import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -61,6 +62,18 @@ public final class ZenModeDisplayLinkPreferenceControllerTest { mContext, "something", mBackend, mHelperBackend); } + @Test + public void testUpdateState_disabled() { + Preference preference = mock(Preference.class); + ZenMode zenMode = new TestModeBuilder() + .setEnabled(false) + .build(); + + mController.updateState(preference, zenMode); + + verify(preference).setEnabled(false); + } + @Test @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java index 7fa4f9f0e71..8aa87e6c903 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java @@ -61,6 +61,18 @@ public final class ZenModeOtherLinkPreferenceControllerTest { mContext, "something", mHelperBackend); } + @Test + public void updateState_disabled() { + CircularIconsPreference pref = mock(CircularIconsPreference.class); + ZenMode zenMode = new TestModeBuilder() + .setEnabled(false) + .build(); + + mController.updateZenMode(pref, zenMode); + + verify(pref).setEnabled(false); + } + @Test public void updateState_loadsSummary() { CircularIconsPreference pref = mock(CircularIconsPreference.class); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java index 8ec980d30aa..63068fa201f 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java @@ -111,6 +111,17 @@ public final class ZenModePeopleLinkPreferenceControllerTest { anyBoolean())).thenReturn(new ColorDrawable(Color.BLACK)); } + @Test + public void updateState_disabled() { + ZenMode zenMode = new TestModeBuilder() + .setEnabled(false) + .build(); + + mController.updateState(mPreference, zenMode); + + assertThat(mPreference.isEnabled()).isFalse(); + } + @Test public void updateState_setsSummary() { mController.updateState(mPreference, TestModeBuilder.EXAMPLE); From 44c25ed47aad680ae465f5c5ac4817dd56c5165d Mon Sep 17 00:00:00 2001 From: danielwbhuang Date: Wed, 24 Jul 2024 18:02:10 +0800 Subject: [PATCH 07/10] Add accessibility appearance related B&R function for Onboarding Add entrance for B&R accessibility appearance related settings. Flag: com.android.settings.flags.accessibility_appearance_settings_backup_enabled Bug: 349941894 Bug: 328585994 Test: manual, need to open aconfig flag. Change-Id: I9617da947201def01f2cfd7f20ce3d11d65f882d --- .../settings/backup/SettingsBackupHelper.java | 15 +++++++++++++-- .../onboarding/OnboardingFeatureProvider.kt | 8 ++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/backup/SettingsBackupHelper.java b/src/com/android/settings/backup/SettingsBackupHelper.java index 556ab725c28..73760a42a44 100644 --- a/src/com/android/settings/backup/SettingsBackupHelper.java +++ b/src/com/android/settings/backup/SettingsBackupHelper.java @@ -28,19 +28,30 @@ import com.android.settingslib.datastore.BackupRestoreStorageManager; /** Backup agent for Settings APK */ public class SettingsBackupHelper extends BackupAgentHelper { public static final String SOUND_BACKUP_HELPER = "SoundSettingsBackup"; + public static final String ACCESSIBILITY_APPEARANCE_BACKUP_HELPER = + "AccessibilityAppearanceSettingsBackup"; @Override public void onCreate() { super.onCreate(); BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this); + OnboardingFeatureProvider onboardingFeatureProvider = + FeatureFactory.getFeatureFactory().getOnboardingFeatureProvider(); + if (Flags.enableSoundBackup()) { - OnboardingFeatureProvider onboardingFeatureProvider = - FeatureFactory.getFeatureFactory().getOnboardingFeatureProvider(); if (onboardingFeatureProvider != null) { addHelper(SOUND_BACKUP_HELPER, onboardingFeatureProvider. getSoundBackupHelper(this, this.getBackupRestoreEventLogger())); } } + + if (Flags.accessibilityAppearanceSettingsBackupEnabled()) { + if (onboardingFeatureProvider != null) { + addHelper(ACCESSIBILITY_APPEARANCE_BACKUP_HELPER, + onboardingFeatureProvider.getAccessibilityAppearanceBackupHelper( + this, this.getBackupRestoreEventLogger())); + } + } } @Override diff --git a/src/com/android/settings/onboarding/OnboardingFeatureProvider.kt b/src/com/android/settings/onboarding/OnboardingFeatureProvider.kt index f76e29a8353..58afb25b92d 100644 --- a/src/com/android/settings/onboarding/OnboardingFeatureProvider.kt +++ b/src/com/android/settings/onboarding/OnboardingFeatureProvider.kt @@ -39,4 +39,12 @@ interface OnboardingFeatureProvider { * @param logger To log B&R stats. */ fun getSoundBackupHelper(context: Context, logger: BackupRestoreEventLogger): BackupHelper + + /** + * Return a BackupHelper for backup accessibility appearance related settings. + * + * @param context App context + * @param logger To log B&R stats. + */ + fun getAccessibilityAppearanceBackupHelper(context: Context, logger: BackupRestoreEventLogger): BackupHelper } \ No newline at end of file From fbb56c4d1caefcc98b9faea33851cd01a0be424b Mon Sep 17 00:00:00 2001 From: tomhsu Date: Fri, 7 Jun 2024 06:52:46 +0000 Subject: [PATCH 08/10] Fix crash if trying to erase the FRP data. - Once factory reset protection is on, user data shall not be erased. Flag: EXEMPT bug fix Fix: 345587437 Test: Manual test passed Test: atest passed Change-Id: I9506224cae7ba12c4181f2b2c676e5eb8ec27431 --- src/com/android/settings/MainClearConfirm.java | 8 ++++++-- .../src/com/android/settings/MainClearConfirmTest.java | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/MainClearConfirm.java b/src/com/android/settings/MainClearConfirm.java index 042c5c26c1a..a5fbebf6e8e 100644 --- a/src/com/android/settings/MainClearConfirm.java +++ b/src/com/android/settings/MainClearConfirm.java @@ -89,8 +89,7 @@ public class MainClearConfirm extends InstrumentedFragment { final PersistentDataBlockManager pdbManager; // pre-flight check hardware support PersistentDataBlockManager if (!SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) { - pdbManager = (PersistentDataBlockManager) - getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); + pdbManager = getActivity().getSystemService(PersistentDataBlockManager.class); } else { pdbManager = null; } @@ -152,6 +151,11 @@ public class MainClearConfirm extends InstrumentedFragment { return false; } + // Do not try to erase factory reset protection data if the protection is alive. + if (pdbManager.isFactoryResetProtectionActive()) { + return false; + } + // The persistent data block will persist if the device is still being provisioned. if (isDeviceStillBeingProvisioned()) { return false; diff --git a/tests/robotests/src/com/android/settings/MainClearConfirmTest.java b/tests/robotests/src/com/android/settings/MainClearConfirmTest.java index 6d85368d0b8..f7711c81311 100644 --- a/tests/robotests/src/com/android/settings/MainClearConfirmTest.java +++ b/tests/robotests/src/com/android/settings/MainClearConfirmTest.java @@ -77,6 +77,7 @@ public class MainClearConfirmTest { when(mMockActivity.getSystemService(Context.DEVICE_POLICY_SERVICE)) .thenReturn(mDevicePolicyManager); + when(mPersistentDataBlockManager.isFactoryResetProtectionActive()).thenReturn(false); } @Test @@ -112,6 +113,13 @@ public class MainClearConfirmTest { assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(null)).isFalse(); } + @Test + public void shouldWipePersistentDataBlock_frpIsAlive_shouldReturnFalse() { + when(mPersistentDataBlockManager.isFactoryResetProtectionActive()).thenReturn(true); + assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(mPersistentDataBlockManager)) + .isFalse(); + } + @Test public void shouldWipePersistentDataBlock_deviceIsStillBeingProvisioned_shouldReturnFalse() { doReturn(true).when(mMainClearConfirm).isDeviceStillBeingProvisioned(); From 0395a8035a0116be764ac2023fc75d42f0285b14 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Thu, 25 Jul 2024 16:07:28 +0800 Subject: [PATCH 09/10] [Audiosharing] Use a11y compliant color for dialog buttons Fix: 353907942 Test: manual Flag: com.android.settingslib.flags.enable_le_audio_sharing Change-Id: Ib034f66db0e3685458c51e0a9cbe9b534c205a69 --- res/drawable/audio_sharing_rounded_bg.xml | 6 ++++-- res/layout/audio_sharing_device_item.xml | 2 ++ res/layout/dialog_custom_body_audio_sharing.xml | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/res/drawable/audio_sharing_rounded_bg.xml b/res/drawable/audio_sharing_rounded_bg.xml index db1e1bbe3fb..35517ea0ec1 100644 --- a/res/drawable/audio_sharing_rounded_bg.xml +++ b/res/drawable/audio_sharing_rounded_bg.xml @@ -15,8 +15,10 @@ ~ limitations under the License. --> - - + \ No newline at end of file diff --git a/res/layout/audio_sharing_device_item.xml b/res/layout/audio_sharing_device_item.xml index 04ecdd758af..c1720e88e67 100644 --- a/res/layout/audio_sharing_device_item.xml +++ b/res/layout/audio_sharing_device_item.xml @@ -17,6 +17,7 @@ @@ -27,6 +28,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/audio_sharing_rounded_bg_ripple" android:textAlignment="center" /> diff --git a/res/layout/dialog_custom_body_audio_sharing.xml b/res/layout/dialog_custom_body_audio_sharing.xml index 2e8c506a688..528bfbb43a7 100644 --- a/res/layout/dialog_custom_body_audio_sharing.xml +++ b/res/layout/dialog_custom_body_audio_sharing.xml @@ -15,7 +15,9 @@ ~ limitations under the License. --> - @@ -60,6 +62,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="4dp" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/audio_sharing_rounded_bg_ripple" android:visibility="gone" /> @@ -70,6 +73,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="4dp" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/audio_sharing_rounded_bg_ripple" android:visibility="gone" /> From 01ecf7624740fcb830c76073e5c8b0364fb79eae Mon Sep 17 00:00:00 2001 From: Yuchen Sun Date: Thu, 25 Jul 2024 06:40:26 +0000 Subject: [PATCH 10/10] Add content description to QR code image for talkback. - Rename string name for reuse purpose. Test: Visual Flag: EXEMPT bug fix Bug: 350603175 Change-Id: Id5b1383c9cf1c2509a621bb9b225e0efeb58ca7f --- res/layout-land/bluetooth_audio_streams_qr_code.xml | 2 +- res/layout/bluetooth_audio_streams_qr_code.xml | 2 +- res/values/strings.xml | 4 ++-- .../settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/res/layout-land/bluetooth_audio_streams_qr_code.xml b/res/layout-land/bluetooth_audio_streams_qr_code.xml index b35bc65f84f..17151979074 100644 --- a/res/layout-land/bluetooth_audio_streams_qr_code.xml +++ b/res/layout-land/bluetooth_audio_streams_qr_code.xml @@ -48,7 +48,7 @@ android:id="@+id/qrcode_view" android:layout_width="@dimen/qrcode_size" android:layout_height="@dimen/qrcode_size" - android:contentDescription="@string/audio_streams_qr_code_page_image_label" + android:contentDescription="@string/qr_code_content_description" android:focusable="true" /> Preview + + QR code Make smaller @@ -13760,8 +13762,6 @@ Scan an audio stream QR code to listen with %1$s Can\u0027t edit password while sharing. To change the password, first turn off audio sharing. - - QR code QR code scanner diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java index 42a74dddc7f..40cf24d52eb 100644 --- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java +++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java @@ -114,6 +114,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment { super.onViewCreated(view, savedInstanceState); mQrCodeView = view.findViewById(R.id.qrcode_view); + mQrCodeView.setContentDescription(getString(R.string.qr_code_content_description)); final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity(); if (wifiNetworkConfig.isHotspot()) {