From 9e69628bd3e734e881d4cfa721ba95b23dce1ee5 Mon Sep 17 00:00:00 2001 From: Owner Cleanup Bot Date: Tue, 17 Dec 2024 09:17:41 -0800 Subject: [PATCH 01/20] [owners] Remove saumyap@google.com from src/com/android/settings/privatespace/OWNERS This suggested change is automatically generated based on group memberships and affiliations. If this change is unnecessary or in error, vote the lowest CR value (i.e. reject the CL) and the bot will abandon it. Vote the highest CR to approve this change. You may also abandon this change. See the owner's recent activity for context: https://android-review.googlesource.com/q/saumyap@google.com To report an issue, file a bug in the Infra>Codereview component. Change-Id: Ic88239b24aa075ab2c475498648fd204036f72df --- src/com/android/settings/privatespace/OWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/settings/privatespace/OWNERS b/src/com/android/settings/privatespace/OWNERS index 158c26a57fb..a22bb0cc9ab 100644 --- a/src/com/android/settings/privatespace/OWNERS +++ b/src/com/android/settings/privatespace/OWNERS @@ -8,4 +8,3 @@ himanshuz@google.com jigarthakkar@google.com josephpv@google.com onshimiye@google.com -saumyap@google.com From 9e74fdbf0df7b40d3ad386c105bced0e664891b3 Mon Sep 17 00:00:00 2001 From: Lais Andrade Date: Tue, 14 Jan 2025 09:48:34 -0800 Subject: [PATCH 02/20] Migrate SeekBarPreference to SliderPreference for haptics Bug: 349661486 Change-Id: I03d9b4973c0eea65c0394a292a43663a7f6dcd06 Flag: EXEMPT flag by System prop Test: manually check the UI Test: VibrationIntensityPreferenceControllerTest --- ...ssibility_vibration_intensity_settings.xml | 10 ++-- ...ibrationIntensityPreferenceController.java | 7 +-- .../core/SliderPreferenceController.java | 3 ++ ...tionIntensityPreferenceControllerTest.java | 24 ++++----- ...backIntensityPreferenceControllerTest.java | 32 +++++------ ...tionIntensityPreferenceControllerTest.java | 24 ++++----- ...tionIntensityPreferenceControllerTest.java | 32 +++++------ ...tionIntensityPreferenceControllerTest.java | 40 +++++++------- ...tionIntensityPreferenceControllerTest.java | 53 ++++++++++--------- 9 files changed, 115 insertions(+), 110 deletions(-) diff --git a/res/xml/accessibility_vibration_intensity_settings.xml b/res/xml/accessibility_vibration_intensity_settings.xml index ba1bd83da94..17e9f93b7b9 100644 --- a/res/xml/accessibility_vibration_intensity_settings.xml +++ b/res/xml/accessibility_vibration_intensity_settings.xml @@ -29,7 +29,7 @@ android:key="vibration_intensity_category_call" android:title="@string/accessibility_call_vibration_category_title"> - - - - - mPreferenceConfig.getSummary()); preference.setMin(getMin()); preference.setMax(getMax()); + preference.setSliderIncrement(1); // Discrete slider // Haptics previews played by the Settings app don't bypass user settings to be played. // The sliders continuously updates the intensity value so the previews can apply them. - preference.setContinuousUpdates(true); + preference.setUpdatesContinuously(true); } @Override diff --git a/src/com/android/settings/core/SliderPreferenceController.java b/src/com/android/settings/core/SliderPreferenceController.java index 9cd4697eae6..79385c1699d 100644 --- a/src/com/android/settings/core/SliderPreferenceController.java +++ b/src/com/android/settings/core/SliderPreferenceController.java @@ -42,6 +42,9 @@ public abstract class SliderPreferenceController extends BasePreferenceControlle } else if (preference instanceof androidx.preference.SeekBarPreference) { ((androidx.preference.SeekBarPreference) preference) .setValue(getSliderPosition()); + } else if (preference instanceof com.android.settingslib.widget.SliderPreference) { + ((com.android.settingslib.widget.SliderPreference) preference) + .setValue(getSliderPosition()); } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AlarmVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AlarmVibrationIntensityPreferenceControllerTest.java index 4a791e3fd9e..f7da254513b 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AlarmVibrationIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AlarmVibrationIntensityPreferenceControllerTest.java @@ -32,8 +32,8 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.SliderPreference; import org.junit.Before; import org.junit.Test; @@ -56,7 +56,7 @@ public class AlarmVibrationIntensityPreferenceControllerTest { private Context mContext; private Vibrator mVibrator; private AlarmVibrationIntensityPreferenceController mController; - private SeekBarPreference mPreference; + private SliderPreference mPreference; @Before public void setUp() { @@ -69,7 +69,7 @@ public class AlarmVibrationIntensityPreferenceControllerTest { mController = new AlarmVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); mLifecycle.addObserver(mController); - mPreference = new SeekBarPreference(mContext); + mPreference = new SliderPreference(mContext); mPreference.setSummary("Test summary"); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); mController.displayPreference(mScreen); @@ -91,7 +91,7 @@ public class AlarmVibrationIntensityPreferenceControllerTest { mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo( + assertThat(mPreference.getValue()).isEqualTo( mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM)); } @@ -101,17 +101,17 @@ public class AlarmVibrationIntensityPreferenceControllerTest { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); } @@ -119,25 +119,25 @@ public class AlarmVibrationIntensityPreferenceControllerTest { public void updateState_shouldDisplayIntensityInSliderPosition() { updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_updatesIntensitySetting() throws Exception { + public void setSliderPosition_updatesIntensitySetting() throws Exception { mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(readSetting(Settings.System.ALARM_VIBRATION_INTENSITY)) .isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); diff --git a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java index e4faaaad0cc..be34b0d5461 100644 --- a/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/HapticFeedbackIntensityPreferenceControllerTest.java @@ -32,8 +32,8 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.SliderPreference; import org.junit.Before; import org.junit.Test; @@ -58,7 +58,7 @@ public class HapticFeedbackIntensityPreferenceControllerTest { private Context mContext; private Vibrator mVibrator; private HapticFeedbackIntensityPreferenceController mController; - private SeekBarPreference mPreference; + private SliderPreference mPreference; @Before public void setUp() { @@ -71,7 +71,7 @@ public class HapticFeedbackIntensityPreferenceControllerTest { mController = new HapticFeedbackIntensityPreferenceController(mContext, PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); mLifecycle.addObserver(mController); - mPreference = new SeekBarPreference(mContext); + mPreference = new SliderPreference(mContext); mPreference.setSummary("Test summary"); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); mController.displayPreference(mScreen); @@ -91,7 +91,7 @@ public class HapticFeedbackIntensityPreferenceControllerTest { Settings.System.putString(mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_INTENSITY, /* value= */ null); mController.updateState(mPreference); - assertThat(mPreference.getProgress()) + assertThat(mPreference.getValue()) .isEqualTo(mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH)); } @@ -101,17 +101,17 @@ public class HapticFeedbackIntensityPreferenceControllerTest { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); } @@ -121,44 +121,44 @@ public class HapticFeedbackIntensityPreferenceControllerTest { updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test public void updateState_shouldDisplayIntensityInSliderPosition() { updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_updatesIntensityAndDependentSettings() throws Exception { + public void setSliderPosition_updatesIntensityAndDependentSettings() throws Exception { mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(readSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY)) .isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); diff --git a/tests/robotests/src/com/android/settings/accessibility/MediaVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MediaVibrationIntensityPreferenceControllerTest.java index da8059c25ec..2a4834c9bf1 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MediaVibrationIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MediaVibrationIntensityPreferenceControllerTest.java @@ -34,8 +34,8 @@ import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.SliderPreference; import org.junit.After; import org.junit.Before; @@ -60,7 +60,7 @@ public class MediaVibrationIntensityPreferenceControllerTest { private Context mContext; private Vibrator mVibrator; private MediaVibrationIntensityPreferenceController mController; - private SeekBarPreference mPreference; + private SliderPreference mPreference; @Before public void setUp() { @@ -73,7 +73,7 @@ public class MediaVibrationIntensityPreferenceControllerTest { mController = new MediaVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); mLifecycle.addObserver(mController); - mPreference = new SeekBarPreference(mContext); + mPreference = new SliderPreference(mContext); mPreference.setSummary("Test summary"); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); mController.displayPreference(mScreen); @@ -100,7 +100,7 @@ public class MediaVibrationIntensityPreferenceControllerTest { mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo( + assertThat(mPreference.getValue()).isEqualTo( mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_MEDIA)); } @@ -110,17 +110,17 @@ public class MediaVibrationIntensityPreferenceControllerTest { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); } @@ -128,25 +128,25 @@ public class MediaVibrationIntensityPreferenceControllerTest { public void updateState_shouldDisplayIntensityInSliderPosition() { updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_updatesIntensitySetting() throws Exception { + public void setSliderPosition_updatesIntensitySetting() throws Exception { mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(readSetting(Settings.System.MEDIA_VIBRATION_INTENSITY)) .isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); diff --git a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java index a9bd2d06bad..f372bb8c34b 100644 --- a/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/NotificationVibrationIntensityPreferenceControllerTest.java @@ -30,10 +30,11 @@ import android.provider.Settings; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.SliderPreference; import org.junit.Before; import org.junit.Test; @@ -56,7 +57,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest { private Context mContext; private Vibrator mVibrator; private NotificationVibrationIntensityPreferenceController mController; - private SeekBarPreference mPreference; + private SliderPreference mPreference; @Before public void setUp() { @@ -69,7 +70,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest { mController = new NotificationVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); mLifecycle.addObserver(mController); - mPreference = new SeekBarPreference(mContext); + mPreference = new SliderPreference(mContext); mPreference.setSummary("Test summary"); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); mController.displayPreference(mScreen); @@ -89,7 +90,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest { Settings.System.putString(mContext.getContentResolver(), Settings.System.NOTIFICATION_VIBRATION_INTENSITY, /* value= */ null); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo( + assertThat(mPreference.getValue()).isEqualTo( mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION)); } @@ -101,22 +102,21 @@ public class NotificationVibrationIntensityPreferenceControllerTest { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.getSummary()).isNull(); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); - // TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed -// assertThat(mPreference.getSummary()).isNotNull(); -// assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString( -// R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary)); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getSummary()).isNotNull(); + assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString( + R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary)); assertThat(mPreference.isEnabled()).isFalse(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.getSummary()).isNull(); assertThat(mPreference.isEnabled()).isTrue(); } @@ -126,27 +126,27 @@ public class NotificationVibrationIntensityPreferenceControllerTest { updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_updatesIntensitySetting() throws Exception { + public void setSliderPosition_updatesIntensitySetting() throws Exception { mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(readSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY)) .isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); diff --git a/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java index 67f643ac30c..ac218ffc891 100644 --- a/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/RingVibrationIntensityPreferenceControllerTest.java @@ -30,10 +30,11 @@ import android.provider.Settings; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.SliderPreference; import org.junit.Before; import org.junit.Test; @@ -59,7 +60,7 @@ public class RingVibrationIntensityPreferenceControllerTest { private Context mContext; private Vibrator mVibrator; private RingVibrationIntensityPreferenceController mController; - private SeekBarPreference mPreference; + private SliderPreference mPreference; @Before public void setUp() { @@ -72,7 +73,7 @@ public class RingVibrationIntensityPreferenceControllerTest { mController = new RingVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); mLifecycle.addObserver(mController); - mPreference = new SeekBarPreference(mContext); + mPreference = new SliderPreference(mContext); mPreference.setSummary("Test summary"); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); mController.displayPreference(mScreen); @@ -92,7 +93,7 @@ public class RingVibrationIntensityPreferenceControllerTest { Settings.System.putString(mContext.getContentResolver(), Settings.System.RING_VIBRATION_INTENSITY, /* value= */ null); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo( + assertThat(mPreference.getValue()).isEqualTo( mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_RINGTONE)); } @@ -102,22 +103,21 @@ public class RingVibrationIntensityPreferenceControllerTest { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.getSummary()).isNull(); assertThat(mPreference.isEnabled()).isTrue(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); - // TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed -// assertThat(mPreference.getSummary()).isNotNull(); -// assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString( -// R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary)); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getSummary()).isNotNull(); + assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString( + R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary)); assertThat(mPreference.isEnabled()).isFalse(); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.getSummary()).isNull(); assertThat(mPreference.isEnabled()).isTrue(); } @@ -129,44 +129,44 @@ public class RingVibrationIntensityPreferenceControllerTest { updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test public void updateState_shouldDisplayIntensityInSliderPosition() { updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mController.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_updatesIntensityAndDependentSettings() throws Exception { + public void setSliderPosition_updatesIntensityAndDependentSettings() throws Exception { mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(readSetting(Settings.System.RING_VIBRATION_INTENSITY)) .isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationIntensityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationIntensityPreferenceControllerTest.java index 2508345b552..6716e361d16 100644 --- a/tests/robotests/src/com/android/settings/accessibility/VibrationIntensityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/VibrationIntensityPreferenceControllerTest.java @@ -29,8 +29,8 @@ import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; -import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.SliderPreference; import org.junit.Before; import org.junit.Test; @@ -70,7 +70,7 @@ public class VibrationIntensityPreferenceControllerTest { private Lifecycle mLifecycle; private Context mContext; private Vibrator mVibrator; - private SeekBarPreference mPreference; + private SliderPreference mPreference; @Before public void setUp() { @@ -85,7 +85,7 @@ public class VibrationIntensityPreferenceControllerTest { VibrationIntensityPreferenceController controller = createPreferenceController(3); Settings.System.putString(mContext.getContentResolver(), SETTING_KEY, /* value= */ null); controller.updateState(mPreference); - assertThat(mPreference.getProgress()) + assertThat(mPreference.getValue()) .isEqualTo(mVibrator.getDefaultVibrationIntensity(VIBRATION_USAGE)); } @@ -96,17 +96,17 @@ public class VibrationIntensityPreferenceControllerTest { updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, OFF); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(mPreference.isEnabled()).isFalse(); updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); assertThat(mPreference.isEnabled()).isTrue(); } @@ -116,19 +116,19 @@ public class VibrationIntensityPreferenceControllerTest { updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_MEDIUM); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_OFF); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test @@ -137,19 +137,19 @@ public class VibrationIntensityPreferenceControllerTest { updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_MEDIUM); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_OFF); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test @@ -158,39 +158,39 @@ public class VibrationIntensityPreferenceControllerTest { updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_HIGH); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_MEDIUM); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_OFF); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_mainSwitchDisabled_ignoresUpdates() throws Exception { + public void setSliderPosition_mainSwitchDisabled_ignoresUpdates() throws Exception { VibrationIntensityPreferenceController controller = createPreferenceController(3); updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, OFF); controller.updateState(mPreference); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); assertThat(mPreference.isEnabled()).isFalse(); assertThat(controller.setSliderPosition(Vibrator.VIBRATION_INTENSITY_HIGH)).isFalse(); assertThat(readSetting(SETTING_KEY)).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW); - assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); + assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF); } @Test - public void setProgress_allSupportedPositions_updatesIntensitySetting() throws Exception { + public void setSliderPosition_allSupportedPositions_updatesIntensitySetting() throws Exception { VibrationIntensityPreferenceController controller = createPreferenceController(3); controller.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); @@ -207,7 +207,8 @@ public class VibrationIntensityPreferenceControllerTest { } @Test - public void setProgress_twoSupportedPositions_updatesMediumPositionToHigh() throws Exception { + public void setSliderPosition_twoSupportedPositions_updatesMediumPositionToHigh() + throws Exception { VibrationIntensityPreferenceController controller = createPreferenceController(2); controller.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF); @@ -224,7 +225,7 @@ public class VibrationIntensityPreferenceControllerTest { } @Test - public void setProgress_oneSupportedPosition_updatesOnPositionsToDeviceDefault() + public void setSliderPosition_oneSupportedPosition_updatesOnPositionsToDeviceDefault() throws Exception { int defaultIntensity = mVibrator.getDefaultVibrationIntensity(VIBRATION_USAGE); VibrationIntensityPreferenceController controller = createPreferenceController(1); @@ -255,7 +256,7 @@ public class VibrationIntensityPreferenceControllerTest { VibrationIntensityPreferenceController controller = new TestPreferenceController(mContext, supportedIntensityLevels); mLifecycle.addObserver(controller); - mPreference = new SeekBarPreference(mContext); + mPreference = new SliderPreference(mContext); mPreference.setSummary("Test summary"); when(mScreen.findPreference(controller.getPreferenceKey())).thenReturn(mPreference); controller.displayPreference(mScreen); From bf892b5d43d54ece30bf8a06acfca8753cae1764 Mon Sep 17 00:00:00 2001 From: Simon Wingrove Date: Mon, 17 Feb 2025 15:01:47 +0000 Subject: [PATCH 03/20] Update "do not disturb access" to "do not disturb control" As per agreed UXW proposal (see bug) Bug: 396587231 Test: manually Flag: EXEMPT string change Change-Id: Icd737e44abcd09566680c2cd0c2e941f7a09978c --- res/values/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index c5a938e6f18..05beecd7b04 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9239,7 +9239,7 @@ It can turn Do Not Disturb on or off and change related settings. It can manage and activate Modes, and change related settings. - If you turn off notification access for %1$s, Do Not Disturb access may also be turned off. + If you turn off notification access for %1$s, Do Not Disturb control may also be turned off. If you turn off notification access for %1$s, Modes access may also be turned off. @@ -9417,14 +9417,14 @@ The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]--> Tap to get the app - - Do Not Disturb access + + Do Not Disturb control Allow Do Not Disturb - - No installed apps have requested Do Not Disturb access + + No installed apps have requested Do Not Disturb control Modes access @@ -9432,7 +9432,7 @@ Allow Modes access - + No installed apps have requested Modes access From d16be090869288f0a067597cc7d637f99693da2f Mon Sep 17 00:00:00 2001 From: Angela Wang Date: Thu, 20 Feb 2025 10:32:42 +0000 Subject: [PATCH 04/20] Set content description on ambien volume sliders Added content descriptions to the UI component to improve accessibility for TalkBack users. Flag: EXEMPT bugfix Bug: 397134669 Test: manually test with TalkBack Change-Id: If0c40d7b91061da5e100e49e71279adb6f69e77f --- res/values/strings.xml | 6 ++++++ .../settings/bluetooth/AmbientVolumePreference.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index a8963b2eecb..abbd0d38146 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -179,6 +179,12 @@ Left Right + + Surroundings + + Left surroundings + + Right surroundings Mute surroundings diff --git a/src/com/android/settings/bluetooth/AmbientVolumePreference.java b/src/com/android/settings/bluetooth/AmbientVolumePreference.java index 4f89007cf5f..86c8c94f22e 100644 --- a/src/com/android/settings/bluetooth/AmbientVolumePreference.java +++ b/src/com/android/settings/bluetooth/AmbientVolumePreference.java @@ -318,9 +318,16 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV if (side == SIDE_LEFT) { slider.setTitle( getContext().getString(R.string.bluetooth_ambient_volume_control_left)); + slider.setSliderContentDescription(getContext().getString( + R.string.bluetooth_ambient_volume_control_left_description)); } else if (side == SIDE_RIGHT) { slider.setTitle( getContext().getString(R.string.bluetooth_ambient_volume_control_right)); + slider.setSliderContentDescription(getContext().getString( + R.string.bluetooth_ambient_volume_control_right_description)); + } else { + slider.setSliderContentDescription(getContext().getString( + R.string.bluetooth_ambient_volume_control_description)); } mSideToSliderMap.put(side, slider); } From d6f139b0cf935819d179535e844880cacc290560 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Tue, 25 Feb 2025 16:48:46 +0800 Subject: [PATCH 05/20] [Audiosharing] Add join handler activity to manifest Test: compile Flag: com.android.settingslib.flags.promote_audio_sharing_for_second_auto_connected_lea_device Bug: 395786392 Change-Id: I9b59300f399f970ae40ffa6fcfdf7f643e271f62 --- AndroidManifest.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 47d3360fa09..d3c82d6cd5a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5508,6 +5508,24 @@ android:value="com.android.settings.applications.contacts.ContactsStorageSettings"/> + + + + + + + + Date: Mon, 24 Feb 2025 20:01:56 +0000 Subject: [PATCH 06/20] Left & right buttons for display size & text preview Test: manually open page and verify buttons work. Make sure to check the buttons during setup wizard. Bug: 386906497 Flag: EXEMPT xml-dependent change Change-Id: I488d74591d698ad918fd864cf39b2f3133abd463 --- res/drawable/keyboard_arrow_left.xml | 19 +++++++ res/drawable/keyboard_arrow_right.xml | 19 +++++++ .../accessibility_text_reading_preview.xml | 53 ++++++++++++++----- res/values/strings.xml | 4 ++ .../TextReadingPreviewPreference.java | 23 ++++++++ 5 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 res/drawable/keyboard_arrow_left.xml create mode 100644 res/drawable/keyboard_arrow_right.xml diff --git a/res/drawable/keyboard_arrow_left.xml b/res/drawable/keyboard_arrow_left.xml new file mode 100644 index 00000000000..c2ffc1d21cf --- /dev/null +++ b/res/drawable/keyboard_arrow_left.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/res/drawable/keyboard_arrow_right.xml b/res/drawable/keyboard_arrow_right.xml new file mode 100644 index 00000000000..ba763690521 --- /dev/null +++ b/res/drawable/keyboard_arrow_right.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/accessibility_text_reading_preview.xml b/res/layout/accessibility_text_reading_preview.xml index 830d9e66778..2532a7957eb 100644 --- a/res/layout/accessibility_text_reading_preview.xml +++ b/res/layout/accessibility_text_reading_preview.xml @@ -38,20 +38,47 @@ android:text="@string/screen_zoom_preview_title" style="@style/AccessibilityTextReadingPreviewTitle" /> - - - + android:orientation="vertical"> + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 3679ba403ab..bddf6ead093 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -80,6 +80,10 @@ Preview QR code + + Previous preview + + Next preview Make smaller diff --git a/src/com/android/settings/accessibility/TextReadingPreviewPreference.java b/src/com/android/settings/accessibility/TextReadingPreviewPreference.java index 9161171914a..99e71258b6f 100644 --- a/src/com/android/settings/accessibility/TextReadingPreviewPreference.java +++ b/src/com/android/settings/accessibility/TextReadingPreviewPreference.java @@ -22,6 +22,7 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; +import android.widget.ImageButton; import android.widget.LinearLayout; import androidx.preference.Preference; @@ -99,6 +100,28 @@ public class TextReadingPreviewPreference extends Preference { (DotsPageIndicator) holder.findViewById(R.id.page_indicator); updateAdapterIfNeeded(viewPager, pageIndicator, mPreviewAdapter); updatePagerAndIndicator(viewPager, pageIndicator); + viewPager.setClipToOutline(true); + + int layoutDirection = + getContext().getResources().getConfiguration().getLayoutDirection(); + int previousId = (layoutDirection == View.LAYOUT_DIRECTION_RTL) + ? R.id.preview_right_button : R.id.preview_left_button; + int nextId = (layoutDirection == View.LAYOUT_DIRECTION_RTL) + ? R.id.preview_left_button : R.id.preview_right_button; + final ImageButton previousButton = previewLayout.findViewById(previousId); + final ImageButton nextButton = previewLayout.findViewById(nextId); + + // These call ViewPager#setCurrentItem directly + // because that doesn't force a refresh through notifyChanged(). + // We found this avoids a crash in SUW (See b/386906497). + previousButton.setOnClickListener((view) -> + viewPager.setCurrentItem(getCurrentItem() - 1)); + previousButton.setContentDescription(getContext().getString( + R.string.preview_pager_previous_button)); + nextButton.setOnClickListener((view) -> + viewPager.setCurrentItem(getCurrentItem() + 1)); + previousButton.setContentDescription(getContext().getString( + R.string.preview_pager_next_button)); } @Override From a407bb627b3bfab739ecf1cb8f2182ee9ca81c7c Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Mon, 18 Nov 2024 11:48:55 +0800 Subject: [PATCH 07/20] Mark ComposePreference as NormalPaddingMixin Bug: 366336385 Flag: EXEMPT bug fix Test: manual Change-Id: I4094c1b040e3427aa28edddc15fd0e8746ff2a80 --- .../settings/spa/preference/ComposePreference.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/spa/preference/ComposePreference.kt b/src/com/android/settings/spa/preference/ComposePreference.kt index 57aa3866390..81d2b4bceb9 100644 --- a/src/com/android/settings/spa/preference/ComposePreference.kt +++ b/src/com/android/settings/spa/preference/ComposePreference.kt @@ -27,20 +27,25 @@ import androidx.preference.PreferenceViewHolder import com.android.settings.R import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.widget.GroupSectionDividerMixin +import com.android.settingslib.widget.NormalPaddingMixin -open class ComposeGroupSectionPreference @JvmOverloads constructor( +open class ComposeGroupSectionPreference +@JvmOverloads +constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0, ) : ComposePreference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin -open class ComposePreference @JvmOverloads constructor( +open class ComposePreference +@JvmOverloads +constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0, -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), NormalPaddingMixin { private var content: @Composable () -> Unit = {} fun setContent(content: @Composable () -> Unit) { From 5097cbe1ca4ac80dffd327fa2f6a34b9b2a087b0 Mon Sep 17 00:00:00 2001 From: Angela Wang Date: Mon, 24 Feb 2025 09:22:06 +0000 Subject: [PATCH 08/20] Filter out non-discoverable scan result in hearing device pairing page Flag: EXEMPT bugfix Bug: 381027984 Test: atest HearingDevicePairingFragmentTest Test: check with real device Change-Id: I7ab70d5508d71472cbd2d0959bb749599d039058 --- .../HearingDevicePairingFragment.java | 13 +++++++++- .../HearingDevicePairingFragmentTest.java | 24 +++++++++---------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java index c797559c290..71eff45fed5 100644 --- a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java +++ b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java @@ -75,6 +75,8 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY = "persist.bluetooth.showdeviceswithoutnames"; private static final String KEY_AVAILABLE_HEARING_DEVICES = "available_hearing_devices"; + // Flags data type from CSS 1.3 Flags + private static final int BT_DISCOVERABLE_MASK = 0x03; LocalBluetoothManager mLocalManager; @Nullable @@ -322,7 +324,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im }; void handleLeScanResult(ScanResult result) { - if (mCachedDeviceManager == null) { + if (mCachedDeviceManager == null || !isDeviceDiscoverable(result)) { return; } final BluetoothDevice device = result.getDevice(); @@ -505,4 +507,13 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast, Toast.LENGTH_SHORT).show(); } + + boolean isDeviceDiscoverable(ScanResult result) { + final ScanRecord scanRecord = result.getScanRecord(); + if (scanRecord == null) { + return false; + } + final int flags = scanRecord.getAdvertiseFlags(); + return (flags & BT_DISCOVERABLE_MASK) != 0; + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java index db82be6db3a..37076abd799 100644 --- a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java @@ -156,9 +156,7 @@ public class HearingDevicePairingFragmentTest { @Test public void handleLeScanResult_markDeviceAsHearingAid() { - ScanResult scanResult = mock(ScanResult.class); - doReturn(mDevice).when(scanResult).getDevice(); - doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); + ScanResult scanResult = createMockScanResult(); mFragment.handleLeScanResult(scanResult); @@ -167,9 +165,7 @@ public class HearingDevicePairingFragmentTest { @Test public void handleLeScanResult_isAndroidCompatible_addDevice() { - ScanResult scanResult = mock(ScanResult.class); - doReturn(mDevice).when(scanResult).getDevice(); - doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); + ScanResult scanResult = createMockScanResult(); doReturn(true).when(mFragment).isAndroidCompatibleHearingAid(scanResult); mFragment.handleLeScanResult(scanResult); @@ -179,9 +175,7 @@ public class HearingDevicePairingFragmentTest { @Test public void handleLeScanResult_isNotAndroidCompatible_discoverServices() { - ScanResult scanResult = mock(ScanResult.class); - doReturn(mDevice).when(scanResult).getDevice(); - doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); + ScanResult scanResult = createMockScanResult(); doReturn(false).when(mFragment).isAndroidCompatibleHearingAid(scanResult); mFragment.handleLeScanResult(scanResult); @@ -191,9 +185,7 @@ public class HearingDevicePairingFragmentTest { @Test public void handleLeScanResult_alreadyBonded_doNothing() { - ScanResult scanResult = mock(ScanResult.class); - doReturn(mDevice).when(scanResult).getDevice(); - doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); + ScanResult scanResult = createMockScanResult(); doReturn(BluetoothDevice.BOND_BONDED).when(mCachedDevice).getBondState(); mFragment.handleLeScanResult(scanResult); @@ -292,6 +284,14 @@ public class HearingDevicePairingFragmentTest { assertThat(isCompatible).isFalse(); } + private ScanResult createMockScanResult() { + ScanResult scanResult = mock(ScanResult.class); + doReturn(mDevice).when(scanResult).getDevice(); + doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); + doReturn(true).when(mFragment).isDeviceDiscoverable(scanResult); + return scanResult; + } + private ScanResult createAshaScanResult() { ScanResult scanResult = mock(ScanResult.class); ScanRecord scanRecord = mock(ScanRecord.class); From 4b13fb36553566f0e9558b72e579a8074521fbe3 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Tue, 25 Feb 2025 16:44:22 +0800 Subject: [PATCH 09/20] [Audiosharing] Add activity skeleton to handle new connected sink Test: atest Flag: com.android.settingslib.flags.promote_audio_sharing_for_second_auto_connected_lea_device Bug: 395786392 Change-Id: Iff1378af8d9a8aa50ccd02b92630541acb24a1ad --- ...luetooth_le_audio_sharing_join_handler.xml | 19 ++++ .../AudioSharingJoinHandlerActivity.java | 33 +++++++ .../AudioSharingJoinHandlerController.java | 55 +++++++++++ ...ioSharingJoinHandlerDashboardFragment.java | 48 ++++++++++ .../AudioSharingJoinHandlerActivityTest.java | 54 +++++++++++ ...AudioSharingJoinHandlerControllerTest.java | 94 +++++++++++++++++++ ...aringJoinHandlerDashboardFragmentTest.java | 52 ++++++++++ 7 files changed, 355 insertions(+) create mode 100644 res/xml/bluetooth_le_audio_sharing_join_handler.xml create mode 100644 src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java create mode 100644 src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java create mode 100644 src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java create mode 100644 tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java create mode 100644 tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragmentTest.java diff --git a/res/xml/bluetooth_le_audio_sharing_join_handler.xml b/res/xml/bluetooth_le_audio_sharing_join_handler.xml new file mode 100644 index 00000000000..eda29a6e62d --- /dev/null +++ b/res/xml/bluetooth_le_audio_sharing_join_handler.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java new file mode 100644 index 00000000000..0e5297d09b7 --- /dev/null +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 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.connecteddevice.audiosharing; + +import com.android.settings.SettingsActivity; + +public class AudioSharingJoinHandlerActivity extends SettingsActivity { + private static final String TAG = "AudioSharingJoinHandlerActivity"; + + @Override + protected boolean isToolbarEnabled() { + return false; + } + + @Override + protected boolean isValidFragment(String fragmentName) { + return AudioSharingJoinHandlerDashboardFragment.class.getName().equals(fragmentName); + } +} diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java new file mode 100644 index 00000000000..376675fa772 --- /dev/null +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 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.connecteddevice.audiosharing; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.lifecycle.DefaultLifecycleObserver; + +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.flags.Flags; + +public class AudioSharingJoinHandlerController extends BasePreferenceController + implements DefaultLifecycleObserver { + private static final String TAG = "AudioSharingJoinHandlerCtrl"; + private static final String KEY = "audio_sharing_join_handler"; + + public AudioSharingJoinHandlerController(@NonNull Context context, + @NonNull String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return (Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice() + && BluetoothUtils.isAudioSharingUIAvailable(mContext)) + ? AVAILABLE_UNSEARCHABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public int getSliceHighlightMenuRes() { + return 0; + } +} diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java new file mode 100644 index 00000000000..6042e5558d2 --- /dev/null +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 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.connecteddevice.audiosharing; + +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; + +public class AudioSharingJoinHandlerDashboardFragment extends DashboardFragment { + private static final String TAG = "AudioSharingJoinHandlerFrag"; + + @Override + public int getMetricsCategory() { + // TODO: use real enum + return 0; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.bluetooth_le_audio_sharing_join_handler; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + use(AudioSharingJoinHandlerController.class); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java new file mode 100644 index 00000000000..d72a9e06be7 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 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.connecteddevice.audiosharing; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioSharingJoinHandlerActivityTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + private AudioSharingJoinHandlerActivity mActivity; + + @Before + public void setUp() { + mActivity = spy(Robolectric.buildActivity(AudioSharingJoinHandlerActivity.class).get()); + } + + @Test + public void isValidFragment_returnsTrue() { + assertThat(mActivity.isValidFragment( + AudioSharingJoinHandlerDashboardFragment.class.getName())).isTrue(); + } + + @Test + public void isValidFragment_returnsFalse() { + assertThat(mActivity.isValidFragment("")).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java new file mode 100644 index 00000000000..51365e6d977 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 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.connecteddevice.audiosharing; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settingslib.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowBluetoothAdapter.class) +public class AudioSharingJoinHandlerControllerTest { + private static final String PREF_KEY = "audio_sharing_join_handler"; + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + private AudioSharingJoinHandlerController mController; + + @Before + public void setUp() { + ShadowBluetoothAdapter shadowBluetoothAdapter = + Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mController = new AudioSharingJoinHandlerController(mContext, PREF_KEY); + } + + @Test + @EnableFlags({Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE, + Flags.FLAG_ENABLE_LE_AUDIO_SHARING}) + public void getAvailabilityStatus_flagOn() { + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); + } + + @Test + @DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE) + public void getAvailabilityStatus_flagOff() { + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void getPreferenceKey_returnsCorrectKey() { + assertThat(mController.getPreferenceKey()).isEqualTo(PREF_KEY); + } + + @Test + public void getSliceHighlightMenuRes_returnsZero() { + assertThat(mController.getSliceHighlightMenuRes()).isEqualTo(0); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragmentTest.java new file mode 100644 index 00000000000..3fd27b10b11 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragmentTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 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.connecteddevice.audiosharing; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioSharingJoinHandlerDashboardFragmentTest { + private AudioSharingJoinHandlerDashboardFragment mFragment; + + @Before + public void setUp() { + mFragment = new AudioSharingJoinHandlerDashboardFragment(); + } + + @Test + public void getPreferenceScreenResId_returnsCorrectXml() { + assertThat(mFragment.getPreferenceScreenResId()) + .isEqualTo(R.xml.bluetooth_le_audio_sharing_join_handler); + } + + @Test + public void getLogTag_returnsCorrectTag() { + assertThat(mFragment.getLogTag()).isEqualTo("AudioSharingJoinHandlerFrag"); + } + + @Test + public void getMetricsCategory_returnsCorrectCategory() { + assertThat(mFragment.getMetricsCategory()).isEqualTo(0); + } +} From 4a97b31f42c638e6f31e0674b48d5787c568374a Mon Sep 17 00:00:00 2001 From: Candice Date: Tue, 18 Feb 2025 07:33:05 +0000 Subject: [PATCH 10/20] Update the dark theme and EDT description Bug: 315012822 Test: Manually. See screenshot in the bug Test: atest DarkModeTopIntroPreferenceControllerTest Flag: android.view.accessibility.force_invert_color Change-Id: I94ba1af4e49c461978fc6b31e82e44c88ba19109 --- res/values/strings.xml | 3 +- res/xml/dark_mode_settings.xml | 4 +- .../DarkModeTopIntroPreferenceController.java | 59 +++++++++++++ ...kModeTopIntroPreferenceControllerTest.java | 86 +++++++++++++++++++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 src/com/android/settings/display/darkmode/DarkModeTopIntroPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/display/darkmode/DarkModeTopIntroPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 9ded9ad354b..fbc766c2f34 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3164,6 +3164,7 @@ Will turn off when %1$s ends Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off. + Use a dark background to make your screen more comfortable to view and reduce battery usage on some screens. Your theme will change if you have a schedule, when the screen is off. Dark theme is currently following your Bedtime mode schedule @@ -5562,7 +5563,7 @@ Make more apps dark - Automatically convert light theme apps to dark theme + Expands dark theme to more apps. May not work with all apps. Remove animations diff --git a/res/xml/dark_mode_settings.xml b/res/xml/dark_mode_settings.xml index 44bd807b660..f886daf2e5b 100644 --- a/res/xml/dark_mode_settings.xml +++ b/res/xml/dark_mode_settings.xml @@ -22,8 +22,8 @@ + settings:searchable="false" + settings:controller="com.android.settings.display.darkmode.DarkModeTopIntroPreferenceController"/> Date: Wed, 26 Feb 2025 11:04:50 -0800 Subject: [PATCH 11/20] Revert "Temp disable test that contains custom shadow within it" This reverts commit a9e22be8a57bccb522bc1f4f2a9f704f45c7b4dc. Reason for revert: No longer need to ignore this test, as the root cause is found and fixed in aosp/3506812 Change-Id: I56681bde8c0485c46f0cd2b84cb83fee5ea0e760 --- .../src/com/android/settings/wifi/slice/WifiSliceTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java index c6bdebdcc14..c9cf5a2a852 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java @@ -56,7 +56,6 @@ import com.android.wifitrackerlib.WifiEntry; import com.android.wifitrackerlib.WifiEntry.ConnectedState; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,7 +72,6 @@ import org.robolectric.shadows.ShadowBinder; import java.util.ArrayList; import java.util.List; -@Ignore("b/394813533") @Deprecated(forRemoval = true) @RunWith(RobolectricTestRunner.class) @Config(shadows = { From 6a4ac9cdbf1cd51b4ad150aec56d1400f8c7ebef Mon Sep 17 00:00:00 2001 From: Matthew DeVore Date: Wed, 26 Feb 2025 19:07:07 +0000 Subject: [PATCH 12/20] CD updater: add missing call in tests ag/31885242 changed the behavior for registerCallback such that it no longer automatically scheduled an update. This CL adds the missing call in tests. Flag: com.android.settings.flags.display_topology_pane_in_display_list Test: atest ExternalDisplayUpdaterTest.java Test: atest ConnectedDeviceGroupControllerTest.java Bug: b/399273324 Change-Id: If030e7cc98289fe3062c1a638580ec6c6479d97a --- .../connecteddevice/ConnectedDeviceGroupControllerTest.java | 3 +-- .../connecteddevice/display/ExternalDisplayUpdaterTest.java | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java index d754993d998..327e2cae5a2 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java @@ -35,7 +35,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.input.InputManager; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.util.FeatureFlagUtils; import android.view.InputDevice; @@ -54,7 +53,6 @@ import com.android.settings.connecteddevice.stylus.StylusDeviceUpdater; import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.flags.FakeFeatureFlagsImpl; -import com.android.settings.flags.Flags; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -213,6 +211,7 @@ public class ConnectedDeviceGroupControllerTest { mConnectedDeviceGroupController.onStart(); verify(mExternalDisplayUpdater).registerCallback(); + verify(mExternalDisplayUpdater).refreshPreference(); verify(mConnectedBluetoothDeviceUpdater).registerCallback(); verify(mConnectedUsbDeviceUpdater).registerCallback(); verify(mConnectedDockUpdater).registerCallback(); diff --git a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayUpdaterTest.java b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayUpdaterTest.java index 824974ad854..698f0a42e20 100644 --- a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayUpdaterTest.java +++ b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayUpdaterTest.java @@ -63,6 +63,7 @@ public class ExternalDisplayUpdaterTest extends ExternalDisplayTestBase { return null; }).when(mMockedCallback).onDeviceAdded(any()); mUpdater.initPreference(mContext, mMockedInjector); + mUpdater.refreshPreference(); mUpdater.registerCallback(); mHandler.flush(); assertThat(mPreferenceAdded).isNotNull(); @@ -85,6 +86,7 @@ public class ExternalDisplayUpdaterTest extends ExternalDisplayTestBase { return null; }).when(mMockedCallback).onDeviceRemoved(any()); mUpdater.initPreference(mContext, mMockedInjector); + mUpdater.refreshPreference(); mUpdater.registerCallback(); mHandler.flush(); assertThat(mPreferenceAdded).isNotNull(); From f87293fc5dfc19cb7a056200117314576fcd8fc9 Mon Sep 17 00:00:00 2001 From: Matthew DeVore Date: Fri, 14 Feb 2025 14:34:45 +0000 Subject: [PATCH 13/20] Show expanded display settings in list fragment Stop distinguishing between the display list and per-display fragments. Instead, always show all displays that are available. This is closer to how the UI will eventually look, which is each display in a tab. Setting an OnChangeListener for multiple preferences with the same key did not work, even though the conflicting preferences had different parent preferences. Only the listener of the top-most preference was notified even when changing a different one. So this CL changes PrefBasics so that it can set unique keys for otherwise identical preferences. Flag: com.android.settings.flags.display_topology_pane_in_display_list Bug: b/352648432 Test: ExternalDisplayPreferenceFragmentTest Test: set rotation of two overlay displays to different non-default values Change-Id: Idabef1e03afd496bfc955927a2894f14f999a105 --- .../ExternalDisplayPreferenceFragment.java | 307 ++++++------------ ...ExternalDisplayPreferenceFragmentTest.java | 215 +++++------- 2 files changed, 178 insertions(+), 344 deletions(-) diff --git a/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java b/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java index 84fbc05f1c9..75b94395963 100644 --- a/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java +++ b/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java @@ -16,8 +16,6 @@ package com.android.settings.connecteddevice.display; -import static android.view.Display.INVALID_DISPLAY; - import static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.DISPLAY_ID_ARG; import static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.EXTERNAL_DISPLAY_HELP_URL; import static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.EXTERNAL_DISPLAY_NOT_FOUND_RESOURCE; @@ -60,7 +58,6 @@ import com.android.settingslib.display.DisplayDensityUtils; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.IllustrationPreference; import com.android.settingslib.widget.MainSwitchPreference; -import com.android.settingslib.widget.TwoTargetPreference; import java.util.ArrayList; import java.util.HashMap; @@ -87,10 +84,12 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen EXTERNAL_DISPLAY_RESOLUTION(60, "external_display_resolution", R.string.external_display_resolution_settings_title), - // Built-in display link is after per-display settings. + // Built-in display link is before per-display settings. BUILTIN_DISPLAY_LIST(70, "builtin_display_list_preference", R.string.builtin_display_settings_category), + EXTERNAL_DISPLAY_LIST(-1, "external_display_list", null), + // If shown, footer should appear below everything. FOOTER(90, "footer_preference", null); @@ -106,16 +105,27 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen public final String key; @Nullable public final Integer titleResource; - void apply(Preference preference) { + /** + * Applies this basic data to the given preference. + * + * @param preference object whose properties to set + * @param nth if non-null, disambiguates the key so that other preferences can have the same + * basic properties. Does not affect the order. + */ + void apply(Preference preference, @Nullable Integer nth) { if (order != -1) { preference.setOrder(order); } if (titleResource != null) { preference.setTitle(titleResource); } - preference.setKey(key); + preference.setKey(nth == null ? key : keyForNth(nth)); preference.setPersistent(false); } + + String keyForNth(int nth) { + return key + "_" + nth; + } } static final int EXTERNAL_DISPLAY_SETTINGS_RESOURCE = R.xml.external_display_settings; @@ -131,12 +141,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen R.drawable.external_display_mirror_portrait; static final int EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE = R.string.screen_zoom_short_summary; - @VisibleForTesting - static final String PREVIOUSLY_SHOWN_LIST_KEY = "mPreviouslyShownListOfDisplays"; private boolean mStarted; @Nullable - private IllustrationPreference mImagePreference; - @Nullable private Preference mDisplayTopologyPreference; @Nullable private PreferenceCategory mBuiltinDisplayPreference; @@ -156,7 +162,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen scheduleUpdate(); } }; - private boolean mPreviouslyShownListOfDisplays; public ExternalDisplayPreferenceFragment() {} @@ -175,12 +180,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen return EXTERNAL_DISPLAY_HELP_URL; } - @Override - public void onSaveInstanceStateCallback(@NonNull Bundle outState) { - outState.putSerializable(PREVIOUSLY_SHOWN_LIST_KEY, - mPreviouslyShownListOfDisplays); - } - @Override public void onCreateCallback(@Nullable Bundle icicle) { if (mInjector == null) { @@ -191,7 +190,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen @Override public void onActivityCreatedCallback(@Nullable Bundle savedInstanceState) { - restoreState(savedInstanceState); View view = getView(); TextView emptyView = null; if (view != null) { @@ -241,17 +239,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen .setSourceMetricsCategory(getMetricsCategory()).launch(); } - @VisibleForTesting - protected void launchExternalDisplaySettings(final int displayId) { - final Bundle args = new Bundle(); - var context = getPrefContext(); - args.putInt(DISPLAY_ID_ARG, displayId); - new SubSettingLauncher(context) - .setDestination(this.getClass().getName()) - .setArguments(args) - .setSourceMetricsCategory(getMetricsCategory()).launch(); - } - @VisibleForTesting protected void launchBuiltinDisplaySettings() { final Bundle args = new Bundle(); @@ -275,30 +262,33 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen var pref = refresh.findUnusedPreference(PrefBasics.FOOTER.key); if (pref == null) { pref = newFooterPreference(context); - PrefBasics.FOOTER.apply(pref); + PrefBasics.FOOTER.apply(pref, /* nth= */ null); } pref.setTitle(title); refresh.addPreference(pref); } @NonNull - private ListPreference reuseRotationPreference(@NonNull Context context, PrefRefresh refresh) { + private ListPreference reuseRotationPreference(@NonNull Context context, PrefRefresh refresh, + int position) { ListPreference pref = refresh.findUnusedPreference( - PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); + PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(position)); if (pref == null) { pref = new ListPreference(context); - PrefBasics.EXTERNAL_DISPLAY_ROTATION.apply(pref); + PrefBasics.EXTERNAL_DISPLAY_ROTATION.apply(pref, position); } refresh.addPreference(pref); return pref; } @NonNull - private Preference reuseResolutionPreference(@NonNull Context context, PrefRefresh refresh) { - var pref = refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); + private Preference reuseResolutionPreference(@NonNull Context context, PrefRefresh refresh, + int position) { + var pref = refresh.findUnusedPreference( + PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(position)); if (pref == null) { pref = new Preference(context); - PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.apply(pref); + PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.apply(pref, position); } refresh.addPreference(pref); return pref; @@ -306,41 +296,34 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen @NonNull private MainSwitchPreference reuseUseDisplayPreference( - @NonNull Context context, @NonNull PrefRefresh refresh) { + Context context, PrefRefresh refresh, int position) { MainSwitchPreference pref = refresh.findUnusedPreference( - PrefBasics.EXTERNAL_DISPLAY_USE.key); + PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(position)); if (pref == null) { pref = new MainSwitchPreference(context); - PrefBasics.EXTERNAL_DISPLAY_USE.apply(pref); + PrefBasics.EXTERNAL_DISPLAY_USE.apply(pref, position); } refresh.addPreference(pref); return pref; } @NonNull - @VisibleForTesting - IllustrationPreference getIllustrationPreference(@NonNull Context context) { - if (mImagePreference == null) { - mImagePreference = new IllustrationPreference(context); - PrefBasics.ILLUSTRATION.apply(mImagePreference); + private IllustrationPreference reuseIllustrationPreference( + Context context, PrefRefresh refresh) { + IllustrationPreference pref = refresh.findUnusedPreference(PrefBasics.ILLUSTRATION.key); + if (pref == null) { + pref = new IllustrationPreference(context); + PrefBasics.ILLUSTRATION.apply(pref, /* nth= */ null); } - return mImagePreference; - } - - /** - * @return return display id argument of this settings page. - */ - @VisibleForTesting - protected int getDisplayIdArg() { - var args = getArguments(); - return args != null ? args.getInt(DISPLAY_ID_ARG, INVALID_DISPLAY) : INVALID_DISPLAY; + refresh.addPreference(pref); + return pref; } @NonNull private PreferenceCategory getBuiltinDisplayListPreference(@NonNull Context context) { if (mBuiltinDisplayPreference == null) { mBuiltinDisplayPreference = new PreferenceCategory(context); - PrefBasics.BUILTIN_DISPLAY_LIST.apply(mBuiltinDisplayPreference); + PrefBasics.BUILTIN_DISPLAY_LIST.apply(mBuiltinDisplayPreference, /* nth= */ null); } return mBuiltinDisplayPreference; } @@ -356,7 +339,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen @NonNull Preference getDisplayTopologyPreference(@NonNull Context context) { if (mDisplayTopologyPreference == null) { mDisplayTopologyPreference = new DisplayTopologyPreference(context); - PrefBasics.DISPLAY_TOPOLOGY.apply(mDisplayTopologyPreference); + PrefBasics.DISPLAY_TOPOLOGY.apply(mDisplayTopologyPreference, /* nth= */ null); } return mDisplayTopologyPreference; } @@ -366,23 +349,23 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen if (pref == null) { pref = new MirrorPreference(context, DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()); - PrefBasics.MIRROR.apply(pref); + PrefBasics.MIRROR.apply(pref, /* nth= */ null); } refresh.addPreference(pref); } @NonNull private AccessibilitySeekBarPreference reuseSizePreference(Context context, - PrefRefresh refresh, int displayId) { + PrefRefresh refresh, int displayId, int position) { AccessibilitySeekBarPreference pref = - refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); + refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(position)); if (pref == null) { pref = new AccessibilitySeekBarPreference(context, /* attrs= */ null); pref.setIconStart(R.drawable.ic_remove_24dp); pref.setIconStartContentDescription(R.string.screen_zoom_make_smaller_desc); pref.setIconEnd(R.drawable.ic_add_24dp); pref.setIconEndContentDescription(R.string.screen_zoom_make_larger_desc); - PrefBasics.EXTERNAL_DISPLAY_SIZE.apply(pref); + PrefBasics.EXTERNAL_DISPLAY_SIZE.apply(pref, position); setStateForDisplaySizePreference(context, displayId, pref); } @@ -404,74 +387,43 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen preference.setOnSeekBarChangeListener(seekBarChangeHandler); } - private void restoreState(@Nullable Bundle savedInstanceState) { - if (savedInstanceState == null) { - return; - } - mPreviouslyShownListOfDisplays = Boolean.TRUE.equals(savedInstanceState.getSerializable( - PREVIOUSLY_SHOWN_LIST_KEY, Boolean.class)); - } - private void update() { final var screen = getPreferenceScreen(); if (screen == null || mInjector == null || mInjector.getContext() == null) { return; } try (var cleanableScreen = new PrefRefresh(screen)) { - updateScreenForDisplayId(getDisplayIdArg(), cleanableScreen, mInjector.getContext()); + updateScreen(cleanableScreen, mInjector.getContext()); } } - private void updateScreenForDisplayId(final int displayId, - @NonNull final PrefRefresh screen, @NonNull Context context) { - final var displaysToShow = externalDisplaysToShow(displayId); + private void updateScreen(final PrefRefresh screen, Context context) { + final var displaysToShow = externalDisplaysToShow(); - if (displaysToShow.isEmpty() && displayId == INVALID_DISPLAY) { - showTextWhenNoDisplaysToShow(screen, context); - } else if (displaysToShow.size() == 1 - && ((displayId == INVALID_DISPLAY && !mPreviouslyShownListOfDisplays) - || displaysToShow.get(0).getDisplayId() == displayId)) { - showDisplaySettings(displaysToShow.get(0), screen, context); - if (displayId == INVALID_DISPLAY && isTopologyPaneEnabled(mInjector)) { - // Only show the topology pane if the user did not arrive via the displays list. - maybeAddV2Components(context, screen); - } - } else if (displayId == INVALID_DISPLAY) { - // If ever shown a list of displays - keep showing it for consistency after - // disconnecting one of the displays, and only one display is left. - mPreviouslyShownListOfDisplays = true; + if (displaysToShow.isEmpty()) { + showTextWhenNoDisplaysToShow(screen, context, /* position= */ 0); + } else { showDisplaysList(displaysToShow, screen, context); } - updateSettingsTitle(displaysToShow, displayId); - } - private void updateSettingsTitle(@NonNull final List displaysToShow, int displayId) { final Activity activity = getCurrentActivity(); - if (activity == null) { - return; + if (activity != null) { + activity.setTitle(EXTERNAL_DISPLAY_TITLE_RESOURCE); } - if (displaysToShow.size() == 1 && displaysToShow.get(0).getDisplayId() == displayId) { - var displayName = displaysToShow.get(0).getName(); - if (!displayName.isEmpty()) { - activity.setTitle(displayName.substring(0, Math.min(displayName.length(), 40))); - return; - } - } - activity.setTitle(EXTERNAL_DISPLAY_TITLE_RESOURCE); } private void showTextWhenNoDisplaysToShow(@NonNull final PrefRefresh screen, - @NonNull Context context) { + @NonNull Context context, int position) { if (isUseDisplaySettingEnabled(mInjector)) { - addUseDisplayPreferenceNoDisplaysFound(context, screen); + addUseDisplayPreferenceNoDisplaysFound(context, screen, position); } addFooterPreference(context, screen, EXTERNAL_DISPLAY_NOT_FOUND_FOOTER_RESOURCE); } - private static PreferenceCategory getCategoryForDisplay(@NonNull Display display, - @NonNull PrefRefresh screen, @NonNull Context context) { + private static PreferenceCategory reuseDisplayCategory( + PrefRefresh screen, Context context, int position) { // The rest of the settings are in a category with the display name as the title. - String categoryKey = "expanded_display_items_" + display.getDisplayId(); + String categoryKey = PrefBasics.EXTERNAL_DISPLAY_LIST.keyForNth(position); var category = (PreferenceCategory) screen.findUnusedPreference(categoryKey); if (category != null) { @@ -479,45 +431,30 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen } else { category = new PreferenceCategory(context); screen.addPreference(category); - category.setPersistent(false); - category.setKey(categoryKey); - category.setTitle(display.getName()); - category.setOrder(PrefBasics.BUILTIN_DISPLAY_LIST.order + 1); + PrefBasics.EXTERNAL_DISPLAY_LIST.apply(category, position); + category.setOrder(PrefBasics.BUILTIN_DISPLAY_LIST.order + 1 + position); } return category; } - private void showDisplaySettings(@NonNull Display display, @NonNull PrefRefresh screen, - @NonNull Context context) { + private void showDisplaySettings(Display display, PrefRefresh refresh, + Context context, boolean includeV1Helpers, int position) { final var isEnabled = mInjector != null && mInjector.isDisplayEnabled(display); if (isUseDisplaySettingEnabled(mInjector)) { - addUseDisplayPreferenceForDisplay(context, screen, display, isEnabled); + addUseDisplayPreferenceForDisplay(context, refresh, display, isEnabled, position); } if (!isEnabled) { // Skip all other settings return; } final var displayRotation = getDisplayRotation(display.getDisplayId()); - if (!isTopologyPaneEnabled(mInjector)) { - screen.addPreference(updateIllustrationImage(context, displayRotation)); + if (includeV1Helpers) { + addIllustrationImage(context, refresh, displayRotation); } - if (isTopologyPaneEnabled(mInjector)) { - var displayCategory = getCategoryForDisplay(display, screen, context); - try (var categoryRefresh = new PrefRefresh(displayCategory)) { - addDisplaySettings(context, categoryRefresh, display, displayRotation); - } - } else { - addDisplaySettings(context, screen, display, displayRotation); - } - - } - - private void addDisplaySettings(Context context, PrefRefresh refresh, Display display, - int displayRotation) { - addResolutionPreference(context, refresh, display); - addRotationPreference(context, refresh, display, displayRotation); + addResolutionPreference(context, refresh, display, position); + addRotationPreference(context, refresh, display, displayRotation, position); if (isResolutionSettingEnabled(mInjector)) { // Do not show the footer about changing resolution affecting apps. This is not in the // UX design for v2, and there is no good place to put it, since (a) if it is on the @@ -529,13 +466,13 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen // inconsistent with the topology pane, which shows that display. // TODO(b/352648432): probably remove footer once the pane and rest of v2 UI is in // place. - if (!isTopologyPaneEnabled(mInjector)) { + if (includeV1Helpers) { addFooterPreference( context, refresh, EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE); } } if (isDisplaySizeSettingEnabled(mInjector)) { - addSizePreference(context, refresh, display.getDisplayId()); + addSizePreference(context, refresh, display.getDisplayId(), position); } } @@ -555,66 +492,26 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen private void showDisplaysList(@NonNull List displaysToShow, @NonNull PrefRefresh screen, @NonNull Context context) { maybeAddV2Components(context, screen); - int order = PrefBasics.BUILTIN_DISPLAY_LIST.order; + int position = 0; + boolean includeV1Helpers = !isTopologyPaneEnabled(mInjector) && displaysToShow.size() <= 1; for (var display : displaysToShow) { - var pref = getDisplayPreference(context, display, screen, ++order); - pref.setSummary(display.getMode().getPhysicalWidth() + " x " - + display.getMode().getPhysicalHeight()); - } - } - - @VisibleForTesting - static String displayListDisplayCategoryKey(int displayId) { - return "display_list_display_category_" + displayId; - } - - @VisibleForTesting - static String resolutionRotationPreferenceKey(int displayId) { - return "display_id_" + displayId; - } - - private Preference getDisplayPreference(@NonNull Context context, - @NonNull Display display, @NonNull PrefRefresh groupCleanable, int categoryOrder) { - var itemKey = resolutionRotationPreferenceKey(display.getDisplayId()); - var categoryKey = displayListDisplayCategoryKey(display.getDisplayId()); - var category = (PreferenceCategory) groupCleanable.findUnusedPreference(categoryKey); - - if (category != null) { - groupCleanable.addPreference(category); - return category.findPreference(itemKey); - } else { - category = new PreferenceCategory(context); - category.setPersistent(false); - category.setKey(categoryKey); - category.setOrder(categoryOrder); - // Must add the category to the hierarchy before adding its descendants. Otherwise - // the category will not have a preference manager, which causes an exception when a - // child is added to it. - groupCleanable.addPreference(category); - - var prefItem = new DisplayPreference(context, display); - prefItem.setTitle( - context.getString(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.titleResource) + " | " - + context.getString(PrefBasics.EXTERNAL_DISPLAY_ROTATION.titleResource)); - prefItem.setKey(itemKey); - - category.addPreference(prefItem); + var category = reuseDisplayCategory(screen, context, position); category.setTitle(display.getName()); - return prefItem; + try (var refresh = new PrefRefresh(category)) { + // The category may have already been populated if it was retrieved from `screen`, + // but we still need to update resolution and rotation items. + showDisplaySettings(display, refresh, context, includeV1Helpers, position); + } + + position++; } } - private List externalDisplaysToShow(int displayIdToShow) { + private List externalDisplaysToShow() { if (mInjector == null) { return List.of(); } - if (displayIdToShow != INVALID_DISPLAY) { - var display = mInjector.getDisplay(displayIdToShow); - if (display != null && isDisplayAllowed(display, mInjector)) { - return List.of(display); - } - } var displaysToShow = new ArrayList(); for (var display : mInjector.getAllDisplays()) { if (display != null && isDisplayAllowed(display, mInjector)) { @@ -624,16 +521,17 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen return displaysToShow; } - private void addUseDisplayPreferenceNoDisplaysFound(Context context, PrefRefresh refresh) { - final var pref = reuseUseDisplayPreference(context, refresh); + private void addUseDisplayPreferenceNoDisplaysFound(Context context, PrefRefresh refresh, + int position) { + final var pref = reuseUseDisplayPreference(context, refresh, position); pref.setChecked(false); pref.setEnabled(false); pref.setOnPreferenceChangeListener(null); } private void addUseDisplayPreferenceForDisplay(final Context context, - PrefRefresh refresh, final Display display, boolean isEnabled) { - final var pref = reuseUseDisplayPreference(context, refresh); + PrefRefresh refresh, final Display display, boolean isEnabled, int position) { + final var pref = reuseUseDisplayPreference(context, refresh, position); pref.setChecked(isEnabled); pref.setEnabled(true); pref.setOnPreferenceChangeListener((p, newValue) -> { @@ -654,20 +552,19 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen }); } - private Preference updateIllustrationImage(@NonNull final Context context, + private void addIllustrationImage(final Context context, PrefRefresh refresh, final int displayRotation) { - var pref = getIllustrationPreference(context); + var pref = reuseIllustrationPreference(context, refresh); if (displayRotation % 2 == 0) { pref.setLottieAnimationResId(EXTERNAL_DISPLAY_PORTRAIT_DRAWABLE); } else { pref.setLottieAnimationResId(EXTERNAL_DISPLAY_LANDSCAPE_DRAWABLE); } - return pref; } private void addRotationPreference(final Context context, - PrefRefresh refresh, final Display display, final int displayRotation) { - var pref = reuseRotationPreference(context, refresh); + PrefRefresh refresh, final Display display, final int displayRotation, int position) { + var pref = reuseRotationPreference(context, refresh, position); if (mRotationEntries == null || mRotationEntriesValues == null) { mRotationEntries = new String[] { context.getString(R.string.external_display_standard_rotation), @@ -694,8 +591,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen } private void addResolutionPreference(final Context context, PrefRefresh refresh, - final Display display) { - var pref = reuseResolutionPreference(context, refresh); + final Display display, int position) { + var pref = reuseResolutionPreference(context, refresh, position); pref.setSummary(display.getMode().getPhysicalWidth() + " x " + display.getMode().getPhysicalHeight()); pref.setOnPreferenceClickListener((Preference p) -> { @@ -706,8 +603,9 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen pref.setEnabled(isResolutionSettingEnabled(mInjector)); } - private void addSizePreference(final Context context, PrefRefresh refresh, int displayId) { - var pref = reuseSizePreference(context, refresh, displayId); + private void addSizePreference(final Context context, PrefRefresh refresh, int displayId, + int position) { + var pref = reuseSizePreference(context, refresh, displayId, position); pref.setSummary(EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE); pref.setOnPreferenceClickListener( (Preference p) -> { @@ -804,27 +702,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen public void onStopTrackingTouch(@NonNull SeekBar seekBar) {} } - @VisibleForTesting - class DisplayPreference extends TwoTargetPreference - implements Preference.OnPreferenceClickListener { - private final int mDisplayId; - - DisplayPreference(@NonNull final Context context, @NonNull final Display display) { - super(context); - mDisplayId = display.getDisplayId(); - - setPersistent(false); - setOnPreferenceClickListener(this); - } - - @Override - public boolean onPreferenceClick(@NonNull Preference preference) { - launchExternalDisplaySettings(mDisplayId); - writePreferenceClickMetric(preference); - return true; - } - } - private static class PrefRefresh implements AutoCloseable { private final PreferenceGroup mScreen; private final HashMap mUnusedPreferences = new HashMap<>(); diff --git a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java index a87e66b1b96..0b118a78358 100644 --- a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java +++ b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java @@ -21,9 +21,6 @@ import static com.android.settings.connecteddevice.display.ExternalDisplayPrefer import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_NOT_FOUND_FOOTER_RESOURCE; import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_SETTINGS_RESOURCE; import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE; -import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.PREVIOUSLY_SHOWN_LIST_KEY; -import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.displayListDisplayCategoryKey; -import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.resolutionRotationPreferenceKey; import static com.android.settings.flags.Flags.FLAG_DISPLAY_SIZE_CONNECTED_DISPLAY_SETTING; import static com.android.settings.flags.Flags.FLAG_DISPLAY_TOPOLOGY_PANE_IN_DISPLAY_LIST; @@ -53,7 +50,6 @@ import androidx.preference.PreferenceScreen; import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.DisplayPreference; import com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.PrefBasics; import com.android.settingslib.widget.MainSwitchPreference; @@ -67,7 +63,6 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa @Nullable private ExternalDisplayPreferenceFragment mFragment; private int mPreferenceIdFromResource; - private int mDisplayIdArg = INVALID_DISPLAY; private boolean mLaunchedBuiltinSettings; private int mResolutionSelectorDisplayId = INVALID_DISPLAY; @Mock @@ -80,17 +75,20 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa assertThat(mPreferenceIdFromResource).isEqualTo(EXTERNAL_DISPLAY_SETTINGS_RESOURCE); } - private void assertDisplayList(boolean present, int displayId) { - // In display list fragment, there is a combined resolution/rotation preference key. - var category = mPreferenceScreen.findPreference(displayListDisplayCategoryKey(displayId)); - var pref = mPreferenceScreen.findPreference(resolutionRotationPreferenceKey(displayId)); - if (present) { - assertThat(category).isNotNull(); - assertThat(pref).isNotNull(); - } else { - assertThat(category).isNull(); - assertThat(pref).isNull(); + private PreferenceCategory getExternalDisplayCategory(int positionIndex) { + return mPreferenceScreen.findPreference( + PrefBasics.EXTERNAL_DISPLAY_LIST.keyForNth(positionIndex)); + } + + private void assertDisplayListCount(int expectedCount) { + int actualCount = 0; + for (int i = 0; i < mPreferenceScreen.getPreferenceCount(); i++) { + Preference child = mPreferenceScreen.getPreference(i); + if (child.getKey().startsWith(PrefBasics.EXTERNAL_DISPLAY_LIST.key)) { + actualCount++; + } } + assertThat(actualCount).isEqualTo(expectedCount); } @Test @@ -100,27 +98,15 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa var fragment = initFragment(); var outState = new Bundle(); - fragment.onSaveInstanceStateCallback(outState); - assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isFalse(); assertThat(mHandler.getPendingMessages().size()).isEqualTo(1); - // Combined resolution/refresh rate are not available in displays list because the pane is - // disabled (v1 UI). - assertDisplayList(false, EXTERNAL_DISPLAY_ID); - assertDisplayList(false, OVERLAY_DISPLAY_ID); - // Individual resolution preference is not available in displays list. - assertThat(mPreferenceScreen.findPreference( - PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key)) - .isNull(); + assertDisplayListCount(0); verify(mMockedInjector, never()).getAllDisplays(); mHandler.flush(); assertThat(mHandler.getPendingMessages().size()).isEqualTo(0); verify(mMockedInjector).getAllDisplays(); - assertDisplayList(true, EXTERNAL_DISPLAY_ID); - assertDisplayList(true, OVERLAY_DISPLAY_ID); - fragment.onSaveInstanceStateCallback(outState); - assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isTrue(); + assertDisplayListCount(2); Preference pref = mPreferenceScreen.findPreference(PrefBasics.DISPLAY_TOPOLOGY.key); assertThat(pref).isNull(); @@ -143,7 +129,8 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa pref = mPreferenceScreen.findPreference(PrefBasics.MIRROR.key); assertThat(pref).isNotNull(); - assertDisplayList(false, mDisplays[1].getDisplayId()); + assertDisplayListCount(1); + assertThat("" + getExternalDisplayCategory(0).getTitle()).isEqualTo("HDMI"); PreferenceCategory listPref = mPreferenceScreen.findPreference(PrefBasics.BUILTIN_DISPLAY_LIST.key); @@ -168,8 +155,7 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa pref = mPreferenceScreen.findPreference(PrefBasics.MIRROR.key); assertThat(pref).isNull(); - assertDisplayList(false, EXTERNAL_DISPLAY_ID); - assertDisplayList(false, OVERLAY_DISPLAY_ID); + assertDisplayListCount(0); var listPref = mPreferenceScreen.findPreference(PrefBasics.BUILTIN_DISPLAY_LIST.key); assertThat(listPref).isNull(); @@ -179,46 +165,36 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa @UiThreadTest public void testLaunchDisplaySettingFromList() { initFragment(); + doReturn(true).when(mMockedInjector).isDisplayEnabled(any()); mHandler.flush(); - assertDisplayList(true, EXTERNAL_DISPLAY_ID); - assertDisplayList(true, OVERLAY_DISPLAY_ID); - PreferenceCategory display1Category = mPreferenceScreen.findPreference( - displayListDisplayCategoryKey(EXTERNAL_DISPLAY_ID)); - var display1Pref = (DisplayPreference) display1Category.getPreference(0); - PreferenceCategory display2Category = mPreferenceScreen.findPreference( - displayListDisplayCategoryKey(OVERLAY_DISPLAY_ID)); - var display2Pref = (DisplayPreference) display2Category.getPreference(0); - assertThat(display1Pref.getKey()).isEqualTo( - resolutionRotationPreferenceKey(EXTERNAL_DISPLAY_ID)); + assertDisplayListCount(2); + var display1Category = getExternalDisplayCategory(0); + var display2Category = getExternalDisplayCategory(1); assertThat("" + display1Category.getTitle()).isEqualTo("HDMI"); - assertThat("" + display1Pref.getSummary()).isEqualTo("1920 x 1080"); - display1Pref.onPreferenceClick(display1Pref); - assertThat(mDisplayIdArg).isEqualTo(1); - verify(mMockedMetricsLogger).writePreferenceClickMetric(display1Pref); - assertThat(display2Pref.getKey()).isEqualTo( - resolutionRotationPreferenceKey(OVERLAY_DISPLAY_ID)); + var display1Resolution = display1Category.findPreference( + PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0)); + display1Resolution.performClick(); + assertThat(mResolutionSelectorDisplayId).isEqualTo(1); + verify(mMockedMetricsLogger).writePreferenceClickMetric(display1Resolution); + var display2Resolution = display2Category.findPreference( + PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(1)); assertThat("" + display2Category.getTitle()).isEqualTo("Overlay #1"); - assertThat("" + display2Pref.getSummary()).isEqualTo("1240 x 780"); - display2Pref.onPreferenceClick(display2Pref); - assertThat(mDisplayIdArg).isEqualTo(2); - verify(mMockedMetricsLogger).writePreferenceClickMetric(display2Pref); + assertThat("" + display2Resolution.getSummary()).isEqualTo("1240 x 780"); + display2Resolution.performClick(); + assertThat(mResolutionSelectorDisplayId).isEqualTo(2); + verify(mMockedMetricsLogger).writePreferenceClickMetric(display2Resolution); } @Test @UiThreadTest public void testShowDisplayListForOnlyOneDisplay_PreviouslyShownList() { var fragment = initFragment(); - // Previously shown list of displays - fragment.onActivityCreatedCallback(createBundleForPreviouslyShownList()); // Only one display available doReturn(new Display[] {mDisplays[1]}).when(mMockedInjector).getAllDisplays(); mHandler.flush(); int attachedId = mDisplays[1].getDisplayId(); - assertDisplayList(true, attachedId); - assertThat(mPreferenceScreen.findPreference( - resolutionRotationPreferenceKey(attachedId))) - .isNotNull(); - assertDisplayList(false, mDisplays[2].getDisplayId()); + assertDisplayListCount(1); + assertThat("" + getExternalDisplayCategory(0).getTitle()).isEqualTo("HDMI"); } @Test @@ -231,14 +207,15 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa // Init initFragment(); mHandler.flush(); - assertDisplayList(false, mDisplays[1].getDisplayId()); - var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); + assertDisplayListCount(1); + var category = getExternalDisplayCategory(0); + var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0)); assertThat(pref).isNotNull(); - pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); + pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0)); assertThat(pref).isNotNull(); - var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key); + var footerPref = category.findPreference(PrefBasics.FOOTER.key); assertThat(footerPref).isNotNull(); - var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); + var sizePref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0)); assertThat(sizePref).isNull(); assertThat("" + footerPref.getTitle()) .isEqualTo(getText(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE)); @@ -253,15 +230,12 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa // Init initFragment(); mHandler.flush(); - assertDisplayList(false, mDisplays[1].getDisplayId()); - assertDisplayList(false, mDisplays[2].getDisplayId()); - var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); - assertThat(pref).isNotNull(); - pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); - assertThat(pref).isNotNull(); - var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key); + assertDisplayListCount(1); + var category = getExternalDisplayCategory(0); + assertThat("" + category.getTitle()).isEqualTo("HDMI"); + var footerPref = category.findPreference(PrefBasics.FOOTER.key); assertThat(footerPref).isNotNull(); - var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); + var sizePref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0)); assertThat(sizePref).isNotNull(); assertThat("" + footerPref.getTitle()) .isEqualTo(getText(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE)); @@ -270,47 +244,48 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa @Test @UiThreadTest public void testShowOneEnabledDisplay_FewAvailable() { - mDisplayIdArg = 1; doReturn(true).when(mMockedInjector).isDisplayEnabled(any()); initFragment(); - verify(mMockedInjector, never()).getDisplay(anyInt()); + verify(mMockedInjector, never()).getAllDisplays(); mHandler.flush(); - verify(mMockedInjector).getDisplay(mDisplayIdArg); - var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); + verify(mMockedInjector, never()).getDisplay(anyInt()); + verify(mMockedInjector).getAllDisplays(); + var pref = mPreferenceScreen.findPreference( + PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0)); assertThat(pref).isNotNull(); - pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); + pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0)); assertThat(pref).isNotNull(); var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key); - assertThat(footerPref).isNotNull(); - var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); + // No footer for showing multiple displays. + assertThat(footerPref).isNull(); + var sizePref = mPreferenceScreen.findPreference( + PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0)); assertThat(sizePref).isNotNull(); - assertThat("" + footerPref.getTitle()) - .isEqualTo(getText(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE)); } @Test @UiThreadTest public void testShowDisabledDisplay() { - mDisplayIdArg = 1; initFragment(); - verify(mMockedInjector, never()).getDisplay(anyInt()); mHandler.flush(); - verify(mMockedInjector).getDisplay(mDisplayIdArg); - var mainPref = (MainSwitchPreference) mPreferenceScreen.findPreference( - PrefBasics.EXTERNAL_DISPLAY_USE.key); + verify(mMockedInjector, never()).getDisplay(anyInt()); + verify(mMockedInjector).getAllDisplays(); + var category = getExternalDisplayCategory(0); + var mainPref = (MainSwitchPreference) category.findPreference( + PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0)); assertThat(mainPref).isNotNull(); assertThat("" + mainPref.getTitle()).isEqualTo( getText(PrefBasics.EXTERNAL_DISPLAY_USE.titleResource)); assertThat(mainPref.isChecked()).isFalse(); assertThat(mainPref.isEnabled()).isTrue(); assertThat(mainPref.getOnPreferenceChangeListener()).isNotNull(); - var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); + var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0)); assertThat(pref).isNull(); - pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); + pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0)); assertThat(pref).isNull(); - var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key); + var footerPref = category.findPreference(PrefBasics.FOOTER.key); assertThat(footerPref).isNull(); - var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); + var sizePref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0)); assertThat(sizePref).isNull(); } @@ -321,7 +296,7 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa initFragment(); mHandler.flush(); var mainPref = (MainSwitchPreference) mPreferenceScreen.findPreference( - PrefBasics.EXTERNAL_DISPLAY_USE.key); + PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0)); assertThat(mainPref).isNotNull(); assertThat("" + mainPref.getTitle()).isEqualTo( getText(PrefBasics.EXTERNAL_DISPLAY_USE.titleResource)); @@ -337,13 +312,13 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa @Test @UiThreadTest public void testDisplayRotationPreference() { - mDisplayIdArg = 1; + final int displayId = 1; doReturn(true).when(mMockedInjector).isDisplayEnabled(any()); var fragment = initFragment(); mHandler.flush(); - ListPreference pref = - mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); - assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key); + var category = getExternalDisplayCategory(0); + ListPreference pref = category.findPreference( + PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0)); assertThat("" + pref.getTitle()).isEqualTo( getText(PrefBasics.EXTERNAL_DISPLAY_ROTATION.titleResource)); assertThat(pref.getEntries().length).isEqualTo(4); @@ -359,10 +334,10 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa assertThat(pref.getOnPreferenceChangeListener()).isNotNull(); assertThat(pref.isEnabled()).isTrue(); var rotation = 1; - doReturn(true).when(mMockedInjector).freezeDisplayRotation(mDisplayIdArg, rotation); + doReturn(true).when(mMockedInjector).freezeDisplayRotation(displayId, rotation); assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, rotation + "")) .isTrue(); - verify(mMockedInjector).freezeDisplayRotation(mDisplayIdArg, rotation); + verify(mMockedInjector).freezeDisplayRotation(displayId, rotation); assertThat(pref.getValue()).isEqualTo(rotation + ""); verify(mMockedMetricsLogger).writePreferenceClickMetric(pref); } @@ -370,31 +345,30 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa @Test @UiThreadTest public void testDisplayResolutionPreference() { - mDisplayIdArg = 1; + final int displayId = 1; doReturn(true).when(mMockedInjector).isDisplayEnabled(any()); var fragment = initFragment(); mHandler.flush(); - var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); - assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key); + var category = getExternalDisplayCategory(0); + var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0)); assertThat("" + pref.getTitle()).isEqualTo( getText(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.titleResource)); assertThat("" + pref.getSummary()).isEqualTo("1920 x 1080"); assertThat(pref.isEnabled()).isTrue(); assertThat(pref.getOnPreferenceClickListener()).isNotNull(); assertThat(pref.getOnPreferenceClickListener().onPreferenceClick(pref)).isTrue(); - assertThat(mResolutionSelectorDisplayId).isEqualTo(mDisplayIdArg); + assertThat(mResolutionSelectorDisplayId).isEqualTo(displayId); verify(mMockedMetricsLogger).writePreferenceClickMetric(pref); } @Test @UiThreadTest public void testDisplaySizePreference() { - mDisplayIdArg = 1; doReturn(true).when(mMockedInjector).isDisplayEnabled(any()); var fragment = initFragment(); mHandler.flush(); - var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); - assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_SIZE.key); + var category = getExternalDisplayCategory(0); + var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0)); assertThat("" + pref.getTitle()) .isEqualTo(getText(PrefBasics.EXTERNAL_DISPLAY_SIZE.titleResource)); assertThat("" + pref.getSummary()) @@ -408,25 +382,25 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa @Test @UiThreadTest public void testUseDisplayPreference_EnabledDisplay() { - mDisplayIdArg = 1; + final int displayId = 1; doReturn(true).when(mMockedInjector).isDisplayEnabled(any()); - doReturn(true).when(mMockedInjector).enableConnectedDisplay(mDisplayIdArg); - doReturn(true).when(mMockedInjector).disableConnectedDisplay(mDisplayIdArg); + doReturn(true).when(mMockedInjector).enableConnectedDisplay(displayId); + doReturn(true).when(mMockedInjector).disableConnectedDisplay(displayId); var fragment = initFragment(); mHandler.flush(); - MainSwitchPreference pref = - mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_USE.key); - assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_USE.key); + MainSwitchPreference pref = getExternalDisplayCategory(0) + .findPreference(PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0)); + assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0)); assertThat("" + pref.getTitle()) .isEqualTo(getText(PrefBasics.EXTERNAL_DISPLAY_USE.titleResource)); assertThat(pref.isEnabled()).isTrue(); assertThat(pref.isChecked()).isTrue(); assertThat(pref.getOnPreferenceChangeListener()).isNotNull(); assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, false)).isTrue(); - verify(mMockedInjector).disableConnectedDisplay(mDisplayIdArg); + verify(mMockedInjector).disableConnectedDisplay(displayId); assertThat(pref.isChecked()).isFalse(); assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, true)).isTrue(); - verify(mMockedInjector).enableConnectedDisplay(mDisplayIdArg); + verify(mMockedInjector).enableConnectedDisplay(displayId); assertThat(pref.isChecked()).isTrue(); verify(mMockedMetricsLogger, times(2)).writePreferenceClickMetric(pref); } @@ -443,13 +417,6 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa return mFragment; } - @NonNull - private Bundle createBundleForPreviouslyShownList() { - var state = new Bundle(); - state.putBoolean(PREVIOUSLY_SHOWN_LIST_KEY, true); - return state; - } - @NonNull private String getText(int id) { return mContext.getResources().getText(id).toString(); @@ -501,21 +468,11 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa mPreferenceIdFromResource = resource; } - @Override - protected int getDisplayIdArg() { - return mDisplayIdArg; - } - @Override protected void launchResolutionSelector(@NonNull Context context, int displayId) { mResolutionSelectorDisplayId = displayId; } - @Override - protected void launchExternalDisplaySettings(final int displayId) { - mDisplayIdArg = displayId; - } - @Override Preference newFooterPreference(Context context) { return new Preference(context); From cddb8cff1488a32f432d4e5f687161baf9c2a723 Mon Sep 17 00:00:00 2001 From: Yi Jiang Date: Mon, 24 Feb 2025 15:52:26 -0800 Subject: [PATCH 14/20] Make the learn more link more descriptive. Test: atest ScreenTimeoutSettingsTest Bug: 316992953 Flag: EXEMPT bugfix Change-Id: Ib859ee8eccd1b08fc48524bd545347bde7c4fb40 --- res/values/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 80b0e8aad8e..9351e7b6dbf 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6911,7 +6911,8 @@ Other options are disabled by your admin - Learn more + + Learn more about disabled screen timeout options Notification log Notification history From 288a5971d2af2e13b4db29f6be2083538767e869 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Thu, 27 Feb 2025 07:50:10 +0800 Subject: [PATCH 15/20] Fixed accessibility issues in Wi-Fi SSID view for SUW - Keep the Save button enabled at all times - Show "Enter the SSID" to remind the user Bug: 386897596 Flag: EXEMPT bugfix Test: Manual testing atest WifiDialogActivityTest \ WifiConfigControllerTest Change-Id: I577e78c34cbaa0640479adf09a916a526500fe68 --- .../settings/wifi/WifiConfigController.java | 14 +++--- src/com/android/settings/wifi/WifiDialog.java | 14 ++++-- .../settings/wifi/WifiDialogActivity.java | 3 ++ .../settings/wifi/utils/AlertDialogHelper.kt | 49 +++++++++++++++++++ .../settings/wifi/utils/WifiDialogHelper.kt | 36 ++++++++++++++ .../wifi/WifiConfigControllerTest.java | 8 --- .../settings/wifi/WifiDialogActivityTest.java | 10 ++++ 7 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 src/com/android/settings/wifi/utils/AlertDialogHelper.kt create mode 100644 src/com/android/settings/wifi/utils/WifiDialogHelper.kt diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java index 39c77a17924..713dfb2458a 100644 --- a/src/com/android/settings/wifi/WifiConfigController.java +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -545,13 +545,13 @@ public class WifiConfigController implements TextWatcher, && !isValidSaePassword(mPasswordView.getText().toString())))) { passwordInvalid = true; } - if ((mSsidView != null && mSsidView.length() == 0) - // If Accesspoint is not saved, apply passwordInvalid check - || ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid - // If AccessPoint is saved (modifying network) and password is changed, apply - // Invalid password check - || mAccessPoint != null && mAccessPoint.isSaved() && passwordInvalid - && mPasswordView.length() > 0)) { + if ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid) { + // If Accesspoint is not saved, apply passwordInvalid check + enabled = false; + } else if (mAccessPoint != null && mAccessPoint.isSaved() && passwordInvalid + && mPasswordView.length() > 0) { + // If AccessPoint is saved (modifying network) and password is changed, apply + // Invalid password check enabled = false; } else { enabled = ipAndProxyFieldsAreValid(); diff --git a/src/com/android/settings/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java index a1ff1ac9a45..40d22e60fa2 100644 --- a/src/com/android/settings/wifi/WifiDialog.java +++ b/src/com/android/settings/wifi/WifiDialog.java @@ -28,6 +28,8 @@ import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import com.android.settings.R; +import com.android.settings.wifi.utils.SsidInputGroup; +import com.android.settings.wifi.utils.WifiDialogHelper; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.wifi.AccessPoint; @@ -62,6 +64,7 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase, private View mView; private WifiConfigController mController; private boolean mHideSubmitButton; + private WifiDialogHelper mDialogHelper; /** * Creates a WifiDialog with no additional style. It displays as a dialog above the current @@ -115,6 +118,9 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase, if (mAccessPoint == null) { mController.hideForgetButton(); } + + mDialogHelper = new WifiDialogHelper(this, + new SsidInputGroup(getContext(), mView, R.id.ssid_layout, R.id.ssid)); } @SuppressWarnings("MissingSuperCall") // TODO: Fix me @@ -155,9 +161,6 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase, public void onClick(DialogInterface dialogInterface, int id) { if (mListener != null) { switch (id) { - case BUTTON_SUBMIT: - mListener.onSubmit(this); - break; case BUTTON_FORGET: if (WifiUtils.isNetworkLockedDown(getContext(), mAccessPoint.getConfig())) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), @@ -170,6 +173,11 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase, } } + /** Return true to tell the parent activity to call onSubmit before onDismiss. */ + public boolean shouldSubmitBeforeFinish() { + return mDialogHelper.isPositive(); + } + @Override public int getMode() { return mMode; diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index 951277aa9cf..1d431753f32 100644 --- a/src/com/android/settings/wifi/WifiDialogActivity.java +++ b/src/com/android/settings/wifi/WifiDialogActivity.java @@ -346,6 +346,9 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog @Override public void onDismiss(DialogInterface dialogInterface) { mDialog2 = null; + if (mDialog != null && mDialog.shouldSubmitBeforeFinish()) { + onSubmit(mDialog); + } mDialog = null; finish(); } diff --git a/src/com/android/settings/wifi/utils/AlertDialogHelper.kt b/src/com/android/settings/wifi/utils/AlertDialogHelper.kt new file mode 100644 index 00000000000..c50323220b7 --- /dev/null +++ b/src/com/android/settings/wifi/utils/AlertDialogHelper.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2025 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.wifi.utils + +import android.content.DialogInterface +import android.util.Log +import androidx.appcompat.app.AlertDialog + +open class AlertDialogHelper(val alertDialog: AlertDialog) { + + var isPositive = false + + init { + alertDialog.setOnShowListener { + alertDialog.getButton(DialogInterface.BUTTON_POSITIVE)?.setOnClickListener { + onPositiveButtonClicked() + } ?: Log.e(TAG, "Can't get the positive button!") + } + } + + open fun onPositiveButtonClicked() { + if (!canDismiss()) { + Log.w(TAG, "Can't dismiss dialog!") + return + } + isPositive = true + alertDialog.dismiss() + } + + open fun canDismiss() = true + + companion object { + const val TAG = "AlertDialogHelper" + } +} diff --git a/src/com/android/settings/wifi/utils/WifiDialogHelper.kt b/src/com/android/settings/wifi/utils/WifiDialogHelper.kt new file mode 100644 index 00000000000..3b23b1a7e50 --- /dev/null +++ b/src/com/android/settings/wifi/utils/WifiDialogHelper.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2025 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.wifi.utils + +import android.util.Log +import androidx.appcompat.app.AlertDialog + +class WifiDialogHelper( + alertDialog: AlertDialog, + private val ssidInputGroup: SsidInputGroup? = null, +) : AlertDialogHelper(alertDialog) { + + override fun canDismiss(): Boolean { + val isValid = ssidInputGroup?.validate() ?: true + if (!isValid) Log.w(TAG, "SSID is invalid!") + return isValid + } + + companion object { + const val TAG = "WifiDialogHelper" + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java index d80464d5467..d5d395e4452 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java @@ -145,14 +145,6 @@ public class WifiConfigControllerTest { .isEqualTo(View.GONE); } - @Test - public void isSubmittable_noSSID_shouldReturnFalse() { - final TextView ssid = mView.findViewById(R.id.ssid); - assertThat(ssid).isNotNull(); - ssid.setText(""); - assertThat(mController.isSubmittable()).isFalse(); - } - @Test public void isSubmittable_longPsk_shouldReturnFalse() { final TextView password = mView.findViewById(R.id.password); diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java index d1cbd0ee1b7..159d97acd15 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java @@ -349,4 +349,14 @@ public class WifiDialogActivityTest { verify(mActivity).dismissDialog(); } + + @Test + public void onDismiss_shouldSubmitBeforeFinish_callOnSubmit() { + mActivity.mDialog = mWifiDialog; + when(mWifiDialog.shouldSubmitBeforeFinish()).thenReturn(true); + + mActivity.onDismiss(mWifiDialog); + + verify(mActivity).onSubmit(mWifiDialog); + } } From d18b9e070ef7b2a1c7328bea0777728106fe30e1 Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Thu, 27 Feb 2025 12:34:45 +0800 Subject: [PATCH 16/20] [A11y] Fix talkback span string range. - Update SPAN_INCLUSIVE_INCLUSIVE to SPAN_EXCLUSIVE_EXCLUSIVE to avoid impact the other talkback span. Bug: 399528098 Test: Talkback Flag: EXEMPT for simple fix Change-Id: I93b4bbaa0c1745e9f893965fc023848a3fda3479 --- src/com/android/settings/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 636ef1c7154..f01d9ee7af9 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -696,7 +696,7 @@ public final class Utils extends com.android.settingslib.Utils { final SpannableString str = new SpannableString(displayText); str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0, displayText.length(), - Spannable.SPAN_INCLUSIVE_INCLUSIVE); + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return str; } From 66f7c84e0d85ee4a6726948fc2b305e733e8d38a Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Thu, 27 Feb 2025 12:38:22 +0800 Subject: [PATCH 17/20] [A11y] Fix battery chart view annoucement. Bug: 386027256 Test: Talkback Flag: EXEMPT for simple fix Change-Id: I4cb51e982469b8c053d65cfb81aa5d4fed925a0e --- .../batteryusage/BatteryChartPreferenceController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 2edbf99f55a..a248bdff16f 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -259,7 +259,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mDailyChartIndex, mHourlyChartIndex)); refreshUi(); mHandler.post( - () -> mDailyChartView.announceForAccessibility(getAccessibilityAnnounceMessage())); + () -> mDailyChartView.setAccessibilityPaneTitle(getAccessibilityAnnounceMessage())); if (mOnSelectedIndexUpdatedListener != null) { mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated(); } @@ -299,7 +299,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll refreshUi(); mHandler.post( () -> - mDailyChartView.announceForAccessibility( + mDailyChartView.setAccessibilityPaneTitle( getAccessibilityAnnounceMessage())); mMetricsFeatureProvider.action( mPrefContext, @@ -326,7 +326,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll refreshUi(); mHandler.post( () -> - mHourlyChartView.announceForAccessibility( + mHourlyChartView.setAccessibilityPaneTitle( getAccessibilityAnnounceMessage())); mMetricsFeatureProvider.action( mPrefContext, From 51c5c2e4738c931538cef7e0761d98448a4dcfca Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Wed, 26 Feb 2025 17:12:45 +0800 Subject: [PATCH 18/20] [Audiosharing] Handle device connected in handler activity Test: atest Flag: com.android.settingslib.flags.promote_audio_sharing_for_second_auto_connected_lea_device Bug: 395786392 Change-Id: Icea9ad31d3115bf90557e8c2795987cd1d0c824b --- .../AudioSharingJoinHandlerActivity.java | 13 + .../AudioSharingJoinHandlerController.java | 221 ++++++++++++++++- ...ioSharingJoinHandlerDashboardFragment.java | 9 +- .../AudioSharingJoinHandlerActivityTest.java | 40 +++ ...AudioSharingJoinHandlerControllerTest.java | 234 +++++++++++++++++- 5 files changed, 514 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java index 0e5297d09b7..82c099d6cdc 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivity.java @@ -16,11 +16,24 @@ package com.android.settings.connecteddevice.audiosharing; +import android.os.Bundle; + import com.android.settings.SettingsActivity; +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.flags.Flags; public class AudioSharingJoinHandlerActivity extends SettingsActivity { private static final String TAG = "AudioSharingJoinHandlerActivity"; + @Override + protected void onCreate(Bundle savedState) { + super.onCreate(savedState); + if (!Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice() + || !BluetoothUtils.isAudioSharingUIAvailable(this)) { + finish(); + } + } + @Override protected boolean isToolbarEnabled() { return false; diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java index 376675fa772..6b664788d64 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerController.java @@ -16,25 +16,183 @@ package com.android.settings.connecteddevice.audiosharing; +import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.content.Intent; +import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceScreen; +import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.flags.Flags; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; public class AudioSharingJoinHandlerController extends BasePreferenceController - implements DefaultLifecycleObserver { + implements DefaultLifecycleObserver, BluetoothCallback { private static final String TAG = "AudioSharingJoinHandlerCtrl"; private static final String KEY = "audio_sharing_join_handler"; + @Nullable private final LocalBluetoothManager mBtManager; + @Nullable private final BluetoothEventManager mEventManager; + @Nullable private final CachedBluetoothDeviceManager mDeviceManager; + @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant; + private final Executor mExecutor; + @Nullable private DashboardFragment mFragment; + @Nullable private AudioSharingDialogHandler mDialogHandler; + @VisibleForTesting + BluetoothLeBroadcastAssistant.Callback mAssistantCallback = + new BluetoothLeBroadcastAssistant.Callback() { + @Override + public void onSearchStarted(int reason) { + } + + @Override + public void onSearchStartFailed(int reason) { + } + + @Override + public void onSearchStopped(int reason) { + } + + @Override + public void onSearchStopFailed(int reason) { + } + + @Override + public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) { + } + + @Override + public void onSourceAdded( + @NonNull BluetoothDevice sink, int sourceId, int reason) { + Log.d(TAG, "onSourceAdded: dismiss stale dialog."); + if (mDeviceManager != null && mDialogHandler != null) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(sink); + if (cachedDevice != null) { + mDialogHandler.closeOpeningDialogsForLeaDevice(cachedDevice); + } + } + } + + @Override + public void onSourceAddFailed( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastMetadata source, + int reason) { + } + + @Override + public void onSourceModified( + @NonNull BluetoothDevice sink, int sourceId, int reason) { + } + + @Override + public void onSourceModifyFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) { + } + + @Override + public void onSourceRemoved( + @NonNull BluetoothDevice sink, int sourceId, int reason) { + } + + @Override + public void onSourceRemoveFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) { + } + + @Override + public void onReceiveStateChanged( + @NonNull BluetoothDevice sink, + int sourceId, + @NonNull BluetoothLeBroadcastReceiveState state) { + } + }; + public AudioSharingJoinHandlerController(@NonNull Context context, @NonNull String preferenceKey) { super(context, preferenceKey); + mBtManager = Utils.getLocalBtManager(mContext); + mEventManager = mBtManager == null ? null : mBtManager.getEventManager(); + mDeviceManager = mBtManager == null ? null : mBtManager.getCachedDeviceManager(); + mAssistant = mBtManager == null ? null + : mBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile(); + mExecutor = Executors.newSingleThreadExecutor(); } + /** + * Initialize the controller. + * + * @param fragment The fragment to provide the context and metrics category for {@link + * AudioSharingBluetoothDeviceUpdater} and provide the host for dialogs. + */ + public void init(@NonNull DashboardFragment fragment) { + mFragment = fragment; + mDialogHandler = new AudioSharingDialogHandler(mContext, fragment); + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) { + var unused = ThreadUtils.postOnBackgroundThread(() -> { + if (!isAvailable()) { + Log.d(TAG, "Skip onStart(), feature is not supported."); + return; + } + if (mEventManager == null || mDialogHandler == null || mAssistant == null) { + Log.d(TAG, "Skip onStart(), profile is not ready."); + return; + } + Log.d(TAG, "onStart() Register callbacks."); + mEventManager.registerCallback(this); + mAssistant.registerServiceCallBack(mExecutor, mAssistantCallback); + mDialogHandler.registerCallbacks(mExecutor); + }); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + var unused = ThreadUtils.postOnBackgroundThread(() -> { + if (!isAvailable()) { + Log.d(TAG, "Skip onStop(), feature is not supported."); + return; + } + if (mEventManager == null || mDialogHandler == null || mAssistant == null) { + Log.d(TAG, "Skip onStop(), profile is not ready."); + return; + } + Log.d(TAG, "onStop() Unregister callbacks."); + mEventManager.unregisterCallback(this); + mAssistant.unregisterServiceCallBack(mAssistantCallback); + mDialogHandler.unregisterCallbacks(); + }); + } + + @Override public int getAvailabilityStatus() { return (Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice() @@ -52,4 +210,65 @@ public class AudioSharingJoinHandlerController extends BasePreferenceController public int getSliceHighlightMenuRes() { return 0; } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + if (mFragment == null + || mFragment.getActivity() == null + || mFragment.getActivity().getIntent() == null) { + Log.d(TAG, "Skip handleDeviceConnectedFromIntent, fragment intent is null"); + return; + } + Intent intent = mFragment.getActivity().getIntent(); + var unused = + ThreadUtils.postOnBackgroundThread(() -> handleDeviceConnectedFromIntent(intent)); + } + + @Override + public void onProfileConnectionStateChanged( + @NonNull CachedBluetoothDevice cachedDevice, + @ConnectionState int state, + int bluetoothProfile) { + if (mDialogHandler == null || mFragment == null) { + Log.d(TAG, "Ignore onProfileConnectionStateChanged, not init correctly"); + return; + } + // Close related dialogs if the BT remote device is disconnected. + if (state == BluetoothAdapter.STATE_DISCONNECTED) { + boolean isLeAudioSupported = BluetoothUtils.isLeAudioSupported(cachedDevice); + if (isLeAudioSupported + && bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { + mDialogHandler.closeOpeningDialogsForLeaDevice(cachedDevice); + } else if (!isLeAudioSupported && !cachedDevice.isConnected()) { + mDialogHandler.closeOpeningDialogsForNonLeaDevice(cachedDevice); + } + } + } + + /** Handle just connected device via intent. */ + @WorkerThread + public void handleDeviceConnectedFromIntent(@NonNull Intent intent) { + BluetoothDevice device = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE, + BluetoothDevice.class); + CachedBluetoothDevice cachedDevice = + (device == null || mDeviceManager == null) + ? null + : mDeviceManager.findDevice(device); + if (cachedDevice == null) { + Log.d(TAG, "Skip handleDeviceConnectedFromIntent, device is null"); + return; + } + if (mDialogHandler == null) { + Log.d(TAG, "Skip handleDeviceConnectedFromIntent, handler is null"); + return; + } + Log.d(TAG, "handleDeviceConnectedFromIntent, device = " + device.getAnonymizedAddress()); + mDialogHandler.handleDeviceConnected(cachedDevice, /* userTriggered= */ false); + } + + @VisibleForTesting + void setDialogHandler(@Nullable AudioSharingDialogHandler dialogHandler) { + mDialogHandler = dialogHandler; + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java index 6042e5558d2..ff7dab6205f 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerDashboardFragment.java @@ -18,12 +18,16 @@ package com.android.settings.connecteddevice.audiosharing; import android.content.Context; +import androidx.annotation.Nullable; + import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; public class AudioSharingJoinHandlerDashboardFragment extends DashboardFragment { private static final String TAG = "AudioSharingJoinHandlerFrag"; + @Nullable private AudioSharingJoinHandlerController mController; + @Override public int getMetricsCategory() { // TODO: use real enum @@ -43,6 +47,9 @@ public class AudioSharingJoinHandlerDashboardFragment extends DashboardFragment @Override public void onAttach(Context context) { super.onAttach(context); - use(AudioSharingJoinHandlerController.class); + mController = use(AudioSharingJoinHandlerController.class); + if (mController != null) { + mController.init(this); + } } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java index d72a9e06be7..db43e460bb1 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerActivityTest.java @@ -18,7 +18,19 @@ package com.android.settings.connecteddevice.audiosharing; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothStatusCodes; +import android.os.Bundle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; + +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settingslib.flags.Flags; import org.junit.Before; import org.junit.Rule; @@ -28,17 +40,45 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class AudioSharingJoinHandlerActivityTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private ShadowBluetoothAdapter mShadowBluetoothAdapter; private AudioSharingJoinHandlerActivity mActivity; @Before public void setUp() { mActivity = spy(Robolectric.buildActivity(AudioSharingJoinHandlerActivity.class).get()); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mShadowBluetoothAdapter.setEnabled(true); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + } + + @Test + @DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE) + public void onCreate_flagOff_finish() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mActivity.onCreate(new Bundle()); + verify(mActivity).finish(); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, + Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE}) + public void onCreate_flagOn_create() { + mActivity.onCreate(new Bundle()); + verify(mActivity, never()).finish(); } @Test diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java index 51365e6d977..7b8ab6d7b9b 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinHandlerControllerTest.java @@ -18,25 +18,63 @@ package com.android.settings.connecteddevice.audiosharing; import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; +import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.content.Context; +import android.content.Intent; +import android.os.Looper; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; +import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.bluetooth.Utils; +import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settings.testutils.shadow.ShadowFragment; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.BluetoothEventManager; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LeAudioProfile; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.flags.Flags; +import com.google.common.collect.ImmutableList; + +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -44,8 +82,14 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import java.util.concurrent.Executor; + @RunWith(RobolectricTestRunner.class) -@Config(shadows = ShadowBluetoothAdapter.class) +@Config(shadows = { + ShadowBluetoothAdapter.class, + ShadowBluetoothUtils.class, + ShadowFragment.class, +}) public class AudioSharingJoinHandlerControllerTest { private static final String PREF_KEY = "audio_sharing_join_handler"; @@ -55,6 +99,18 @@ public class AudioSharingJoinHandlerControllerTest { public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Spy Context mContext = ApplicationProvider.getApplicationContext(); + private Lifecycle mLifecycle; + private LifecycleOwner mLifecycleOwner; + @Mock + private LocalBluetoothManager mLocalBtManager; + @Mock private BluetoothEventManager mEventManager; + @Mock private LocalBluetoothProfileManager mProfileManager; + @Mock private CachedBluetoothDeviceManager mDeviceManager; + @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; + @Mock private PreferenceScreen mScreen; + @Mock private DashboardFragment mFragment; + @Mock private FragmentActivity mActivity; + @Mock private AudioSharingDialogHandler mDialogHandler; private AudioSharingJoinHandlerController mController; @Before @@ -66,7 +122,67 @@ public class AudioSharingJoinHandlerControllerTest { BluetoothStatusCodes.FEATURE_SUPPORTED); shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; + mLocalBtManager = Utils.getLocalBtManager(mContext); + when(mLocalBtManager.getEventManager()).thenReturn(mEventManager); + when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); + when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mDeviceManager); + when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); mController = new AudioSharingJoinHandlerController(mContext, PREF_KEY); + doReturn(mActivity).when(mFragment).getActivity(); + mController.init(mFragment); + mController.setDialogHandler(mDialogHandler); + } + + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + } + + @Test + @DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE) + public void onStart_flagOff_doNothing() { + mController.onStart(mLifecycleOwner); + verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class)); + verify(mDialogHandler, never()).registerCallbacks(any(Executor.class)); + verify(mAssistant, never()) + .registerServiceCallBack( + any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, + Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE}) + public void onStart_flagOn_registerCallbacks() { + mController.onStart(mLifecycleOwner); + verify(mEventManager).registerCallback(any(BluetoothCallback.class)); + verify(mDialogHandler).registerCallbacks(any(Executor.class)); + verify(mAssistant) + .registerServiceCallBack( + any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); + } + + @Test + @DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE) + public void onStop_flagOff_doNothing() { + mController.onStop(mLifecycleOwner); + verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class)); + verify(mDialogHandler, never()).unregisterCallbacks(); + verify(mAssistant, never()) + .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, + Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE}) + public void onStop_flagOn_unregisterCallbacks() { + mController.onStop(mLifecycleOwner); + verify(mEventManager).unregisterCallback(any(BluetoothCallback.class)); + verify(mDialogHandler).unregisterCallbacks(); + verify(mAssistant) + .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); } @Test @@ -91,4 +207,120 @@ public class AudioSharingJoinHandlerControllerTest { public void getSliceHighlightMenuRes_returnsZero() { assertThat(mController.getSliceHighlightMenuRes()).isEqualTo(0); } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, + Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE}) + public void displayPreference_flagOn_updateDeviceList() { + mController.displayPreference(mScreen); + + } + + @Test + public void onProfileConnectionStateChanged_notDisconnectedProfile_doNothing() { + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + + mController.onProfileConnectionStateChanged( + cachedDevice, BluetoothAdapter.STATE_CONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + verifyNoInteractions(mDialogHandler); + } + + @Test + public void onProfileConnectionStateChanged_leaDeviceDisconnected_closeOpeningDialogsForIt() { + // Test when LEA device LE_AUDIO_BROADCAST_ASSISTANT disconnected. + BluetoothDevice device = mock(BluetoothDevice.class); + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + LeAudioProfile profile = mock(LeAudioProfile.class); + when(profile.isEnabled(device)).thenReturn(true); + when(cachedDevice.getProfiles()).thenReturn(ImmutableList.of(profile)); + when(cachedDevice.isConnected()).thenReturn(true); + when(cachedDevice.getDevice()).thenReturn(device); + + mController.onProfileConnectionStateChanged( + cachedDevice, + BluetoothAdapter.STATE_DISCONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + verify(mDialogHandler).closeOpeningDialogsForLeaDevice(cachedDevice); + } + + @Test + public void + onProfileConnectionStateChanged_classicDeviceDisconnected_closeOpeningDialogsForIt() { + // Test when classic device totally disconnected + BluetoothDevice device = mock(BluetoothDevice.class); + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + LeAudioProfile profile = mock(LeAudioProfile.class); + when(profile.isEnabled(device)).thenReturn(false); + when(cachedDevice.getProfiles()).thenReturn(ImmutableList.of(profile)); + when(cachedDevice.isConnected()).thenReturn(false); + when(cachedDevice.getDevice()).thenReturn(device); + + mController.onProfileConnectionStateChanged( + cachedDevice, BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.A2DP); + verify(mDialogHandler).closeOpeningDialogsForNonLeaDevice(cachedDevice); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, + Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE}) + public void handleDeviceConnectedFromIntent_noDevice_doNothing() { + Intent intent = new Intent(); + doReturn(intent).when(mActivity).getIntent(); + mController.displayPreference(mScreen); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class)); + verify(mDialogHandler, never()) + .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean()); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, + Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE}) + public void handleDeviceClickFromIntent_handle() { + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + BluetoothDevice device = mock(BluetoothDevice.class); + when(mDeviceManager.findDevice(device)).thenReturn(cachedDevice); + Intent intent = new Intent(); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, device); + doReturn(intent).when(mActivity).getIntent(); + mController.displayPreference(mScreen); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mDialogHandler).handleDeviceConnected(cachedDevice, /* userTriggered = */ false); + } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_closeOpeningDialogsForSourceAdded() { + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + BluetoothDevice device = mock(BluetoothDevice.class); + when(mDeviceManager.findDevice(device)).thenReturn(cachedDevice); + // onSourceAdded will dismiss stale dialogs + mController.mAssistantCallback.onSourceAdded(device, /* sourceId= */ + 1, /* reason= */ 1); + + verify(mDialogHandler).closeOpeningDialogsForLeaDevice(cachedDevice); + } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() { + BluetoothDevice device = mock(BluetoothDevice.class); + mController.mAssistantCallback.onSearchStarted(/* reason= */ 1); + mController.mAssistantCallback.onSearchStartFailed(/* reason= */ 1); + mController.mAssistantCallback.onSearchStopped(/* reason= */ 1); + mController.mAssistantCallback.onSearchStopFailed(/* reason= */ 1); + BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); + mController.mAssistantCallback.onReceiveStateChanged(device, /* sourceId= */ 1, state); + mController.mAssistantCallback.onSourceModified(device, /* sourceId= */ 1, /* reason= */ 1); + mController.mAssistantCallback.onSourceModifyFailed(device, /* sourceId= */ 1, /* reason= */ + 1); + BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class); + mController.mAssistantCallback.onSourceFound(metadata); + mController.mAssistantCallback.onSourceLost(/* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + + // Above callbacks won't dismiss stale dialogs + verifyNoInteractions(mDialogHandler); + } } From 619e6c8c782f2d29a88b8870ec1f38095524a800 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Wed, 26 Feb 2025 18:03:18 -0800 Subject: [PATCH 19/20] Uses ThemeHelper#trySetSuwTheme to apply theme for Radactioninterstitial screen. Screenshot: https://hsv.googleplex.com/6310318165721088 Bug: 399237091 Test: manual test on device. Flag: EXEMPT flag by PartnerConfigHelper.isGlifExpressiveEnabled Change-Id: I0f1d8a6ea1cbe8bce79c32b4c039656648f5a99a --- .../android/settings/notification/RedactionInterstitial.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/settings/notification/RedactionInterstitial.java b/src/com/android/settings/notification/RedactionInterstitial.java index 92c9e2e0405..043a672a08f 100644 --- a/src/com/android/settings/notification/RedactionInterstitial.java +++ b/src/com/android/settings/notification/RedactionInterstitial.java @@ -70,8 +70,7 @@ public class RedactionInterstitial extends SettingsActivity { @Override protected void onCreate(Bundle savedInstance) { - setTheme(SetupWizardUtils.getTheme(this, getIntent())); - ThemeHelper.trySetDynamicColor(this); + ThemeHelper.trySetSuwTheme(this); super.onCreate(savedInstance); findViewById(R.id.content_parent).setFitsSystemWindows(false); } From c9d46e652729ee4bf10f9b20da919db75a3e8661 Mon Sep 17 00:00:00 2001 From: Zoey Chen Date: Thu, 27 Feb 2025 06:50:05 +0000 Subject: [PATCH 20/20] [Settings] Update title by UX suggestion Bug: 377664066 Test: manual Flag: EXEMPT refactor Change-Id: Ic6869e614d4464bad64ec44c5973589fb997f598 --- res/values/strings.xml | 4 +++- .../RegionAndNumberingSystemPickerFragment.java | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 16d3964991c..13fe2ad99f7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -521,6 +521,8 @@ Add a language Choose a region + + Number System Region preference @@ -536,7 +538,7 @@ Your device will keep %1$s as a system language - Most apps will use your regional preferences + Choose how your device, websites, and apps display numbers diff --git a/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java b/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java index 9831c137915..72a5c311245 100644 --- a/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java +++ b/src/com/android/settings/localepicker/RegionAndNumberingSystemPickerFragment.java @@ -34,6 +34,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.app.LocaleHelper; @@ -42,7 +43,6 @@ 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.android.settingslib.widget.TopIntroPreference; import com.google.android.material.appbar.AppBarLayout; @@ -75,6 +75,7 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment im private static final String KEY_PREFERENCE_APP_LOCALE_SUGGESTED_LIST = "app_locale_suggested_list"; private static final String KEY_TOP_INTRO_PREFERENCE = "top_intro_region"; + private static final String KEY_PREFERENCE_SCREEN ="key_system_language_picker_page"; private static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view"; @Nullable @@ -121,9 +122,10 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment im } Log.d(TAG, "onCreate, mIsNumberingMode = " + mIsNumberingMode); - if (!mIsNumberingMode) { - mActivity.setTitle(R.string.region_selection_title); - } + + PreferenceScreen screen = findPreference(KEY_PREFERENCE_SCREEN); + screen.setTitle(mIsNumberingMode ? R.string.numbering_system_selection_title + : R.string.region_selection_title); TopIntroPreference topIntroPreference = findPreference(KEY_TOP_INTRO_PREFERENCE); if (topIntroPreference != null && mIsNumberingMode) {