From c2d0b25753194bd41c66f79a6ed41626875e1895 Mon Sep 17 00:00:00 2001 From: Roy Chou Date: Mon, 4 Nov 2024 16:00:16 +0000 Subject: [PATCH 01/13] feat(brightness suw): adjust brightness toggles UI and remove autobrightness standalone page Based on the UX alignment in doc and figma, we remove the autobrightness detailed page to make the consistency with other Vision settings, and update the brightness preference icons and summaries. Bug: 377289685 Flag: com.android.settings.accessibility.add_brightness_settings_in_suw Test: manually Change-Id: If4038de07dec7eeb38d3c057affba737849e23c9 --- res/drawable/ic_brightness_auto.xml | 25 ---- res/drawable/ic_brightness_medium.xml | 25 ---- res/drawable/ic_suw_brightness_auto.xml | 13 +- res/drawable/ic_suw_brightness_level.xml | 13 +- res/values/strings.xml | 4 - ...ccessibility_settings_for_setup_wizard.xml | 3 +- ...tnessPreferenceFragmentForSetupWizard.java | 98 --------------- .../core/gateway/SettingsGateway.java | 2 - ...essPreferenceControllerForSetupWizard.java | 5 + ...sPreferenceFragmentForSetupWizardTest.java | 119 ------------------ 10 files changed, 28 insertions(+), 279 deletions(-) delete mode 100644 res/drawable/ic_brightness_auto.xml delete mode 100644 res/drawable/ic_brightness_medium.xml delete mode 100644 src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java delete mode 100644 tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java diff --git a/res/drawable/ic_brightness_auto.xml b/res/drawable/ic_brightness_auto.xml deleted file mode 100644 index 7ace52b5f63..00000000000 --- a/res/drawable/ic_brightness_auto.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/res/drawable/ic_brightness_medium.xml b/res/drawable/ic_brightness_medium.xml deleted file mode 100644 index 3e778857127..00000000000 --- a/res/drawable/ic_brightness_medium.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - \ No newline at end of file diff --git a/res/drawable/ic_suw_brightness_auto.xml b/res/drawable/ic_suw_brightness_auto.xml index a4221c540d1..93f17f54ac6 100644 --- a/res/drawable/ic_suw_brightness_auto.xml +++ b/res/drawable/ic_suw_brightness_auto.xml @@ -21,6 +21,15 @@ android:height="@dimen/accessibility_icon_size" android:color="@color/accessibility_feature_background"/> - + + + + + \ No newline at end of file diff --git a/res/drawable/ic_suw_brightness_level.xml b/res/drawable/ic_suw_brightness_level.xml index 57bd1883849..4ed6374fbe2 100644 --- a/res/drawable/ic_suw_brightness_level.xml +++ b/res/drawable/ic_suw_brightness_level.xml @@ -21,6 +21,15 @@ android:height="@dimen/accessibility_icon_size" android:color="@color/accessibility_feature_background"/> - + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index b62df6081ee..c4a9b010245 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2850,16 +2850,12 @@ Brightness level Adaptive brightness - - - About adaptive brightness Your screen brightness will automatically adjust to your environment and activities. You can move the slider manually to help adaptive brightness learn your preferences. On Off - Display white balance diff --git a/res/xml/accessibility_settings_for_setup_wizard.xml b/res/xml/accessibility_settings_for_setup_wizard.xml index 25b8a5acf69..250954353f5 100644 --- a/res/xml/accessibility_settings_for_setup_wizard.xml +++ b/res/xml/accessibility_settings_for_setup_wizard.xml @@ -37,11 +37,10 @@ settings:useAdminDisabledSummary="true" settings:userRestriction="no_config_brightness"/> - diff --git a/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java deleted file mode 100644 index 19db2668df9..00000000000 --- a/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.accessibility; - -import static android.app.Activity.RESULT_CANCELED; - -import android.app.settings.SettingsEnums; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceScreen; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.settings.R; -import com.android.settings.display.AutoBrightnessSettings; -import com.android.settingslib.Utils; -import com.android.settingslib.widget.FooterPreference; - -import com.google.android.setupcompat.template.FooterBarMixin; -import com.google.android.setupdesign.GlifPreferenceLayout; - -/** - * Fragment for adaptive brightness settings in the SetupWizard. - */ -public class AutoBrightnessPreferenceFragmentForSetupWizard extends AutoBrightnessSettings { - - private static final String FOOTER_PREFERENCE_KEY = "auto_brightness_footer"; - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - updateFooterContentDescription(); - - if (view instanceof GlifPreferenceLayout) { - final GlifPreferenceLayout layout = (GlifPreferenceLayout) view; - final String title = getContext().getString( - R.string.auto_brightness_title); - final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility); - icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorPrimary)); - AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title, - /* description= */ null, icon); - - final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class); - AccessibilitySetupWizardUtils.setPrimaryButton(getContext(), mixin, R.string.done, - () -> { - setResult(RESULT_CANCELED); - finish(); - }); - } - } - - @NonNull - @Override - public RecyclerView onCreateRecyclerView(@NonNull LayoutInflater inflater, - @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { - if (parent instanceof GlifPreferenceLayout) { - final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent; - return layout.onCreateRecyclerView(inflater, parent, savedInstanceState); - } - return super.onCreateRecyclerView(inflater, parent, savedInstanceState); - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.SUW_ACCESSIBILITY_AUTO_BRIGHTNESS; - } - - private void updateFooterContentDescription() { - final PreferenceScreen screen = getPreferenceScreen(); - final FooterPreference footerPreference = screen.findPreference(FOOTER_PREFERENCE_KEY); - if (footerPreference != null) { - String title = getString(R.string.auto_brightness_content_description_title); - final StringBuilder sb = new StringBuilder(); - sb.append(title).append("\n\n").append(footerPreference.getTitle()); - footerPreference.setContentDescription(sb); - } - } -} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 117364f2e9f..7afff2d21de 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -28,7 +28,6 @@ import com.android.settings.accessibility.AccessibilityDetailsSettingsFragment; import com.android.settings.accessibility.AccessibilityHearingAidsFragment; import com.android.settings.accessibility.AccessibilitySettings; import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard; -import com.android.settings.accessibility.AutoBrightnessPreferenceFragmentForSetupWizard; import com.android.settings.accessibility.CaptioningPropertiesFragment; import com.android.settings.accessibility.ColorAndMotionFragment; import com.android.settings.accessibility.HearingDevicePairingFragment; @@ -265,7 +264,6 @@ public class SettingsGateway { EditShortcutsPreferenceFragment.class.getName(), TextReadingPreferenceFragment.class.getName(), TextReadingPreferenceFragmentForSetupWizard.class.getName(), - AutoBrightnessPreferenceFragmentForSetupWizard.class.getName(), CaptioningPropertiesFragment.class.getName(), ToggleDaltonizerPreferenceFragment.class.getName(), ToggleColorInversionPreferenceFragment.class.getName(), diff --git a/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java b/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java index 7cdf3e0ea35..7afe3ea9d8b 100644 --- a/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java +++ b/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java @@ -41,4 +41,9 @@ public class AutoBrightnessPreferenceControllerForSetupWizard } return super.getAvailabilityStatus(); } + + @Override + public CharSequence getSummary() { + return ""; + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java deleted file mode 100644 index c0b9dbd2104..00000000000 --- a/tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.accessibility; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.settings.SettingsEnums; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; - -import androidx.fragment.app.FragmentFactory; -import androidx.fragment.app.testing.FragmentScenario; -import androidx.lifecycle.Lifecycle; -import androidx.preference.Preference; - -import com.android.settings.R; -import com.android.settingslib.widget.FooterPreference; - -import com.google.android.setupcompat.template.FooterBarMixin; -import com.google.android.setupdesign.GlifLayout; -import com.google.android.setupdesign.GlifPreferenceLayout; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -/** Tests for {@link AutoBrightnessPreferenceFragmentForSetupWizard}. */ -@RunWith(RobolectricTestRunner.class) -public class AutoBrightnessPreferenceFragmentForSetupWizardTest { - - // Same as AutoBrightnessPreferenceFragmentForSetupWizard#FOOTER_PREFERENCE_KEY - private static final String FOOTER_PREFERENCE_KEY = "auto_brightness_footer"; - - private FragmentScenario mFragmentScenario; - - private AutoBrightnessPreferenceFragmentForSetupWizard mFragment; - private GlifLayout mGlifLayout; - - @Before - public void setUp() { - mFragmentScenario = FragmentScenario - .launch( - AutoBrightnessPreferenceFragmentForSetupWizard.class, - /* fragmentArgs= */ (Bundle) null, - R.style.GlifTheme, - /* factory= */ (FragmentFactory) null) - .moveToState(Lifecycle.State.RESUMED); - mFragmentScenario.onFragment(fragment -> mFragment = fragment); - - View view = mFragment.getView(); - assertThat(view).isInstanceOf(GlifPreferenceLayout.class); - mGlifLayout = (GlifLayout) view; - } - - @After - public void tearDown() { - mFragmentScenario.close(); - } - - @Test - public void onViewCreated_verifyGlifHerderText() { - assertThat(mGlifLayout.getHeaderText()) - .isEqualTo(mFragment.getString(R.string.auto_brightness_title)); - } - - @Test - public void onViewCreated_verifyGlifFooter() { - FooterBarMixin footerMixin = mGlifLayout.getMixin(FooterBarMixin.class); - assertThat(footerMixin).isNotNull(); - - Button footerButton = footerMixin.getPrimaryButtonView(); - assertThat(footerButton).isNotNull(); - assertThat(footerButton.getText().toString()).isEqualTo(mFragment.getString(R.string.done)); - - footerButton.performClick(); - assertThat(mFragment.getActivity().isFinishing()).isTrue(); - } - - @Test - public void onViewCreated_verifyFooterPreference() { - Preference pref = mFragment.findPreference(FOOTER_PREFERENCE_KEY); - assertThat(pref).isInstanceOf(FooterPreference.class); - - FooterPreference footerPref = (FooterPreference) pref; - String exactTitle = footerPref.getTitle().toString(); - assertThat(exactTitle).isEqualTo(mFragment.getString(R.string.auto_brightness_description)); - - // Ensure that footer content description has "About XXX" prefix for consistency with other - // accessibility suw pages - String expectedContentDescription = - mFragment.getString(R.string.auto_brightness_content_description_title) - + "\n\n" + exactTitle; - assertThat(footerPref.getContentDescription().toString()) - .isEqualTo(expectedContentDescription); - } - - @Test - public void getMetricsCategory_returnsCorrectCategory() { - assertThat(mFragment.getMetricsCategory()).isEqualTo( - SettingsEnums.SUW_ACCESSIBILITY_AUTO_BRIGHTNESS); - } -} From 3863c67a012eaa1ca754a44d121d75c9cda56c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Fri, 8 Nov 2024 17:24:01 +0100 Subject: [PATCH 02/13] Use full day names in the a11y version of schedules in mode list descriptions Bug: 370358575 Test: atest ZenModesListItemPreferenceTest Flag: android.app.modes_ui Change-Id: I5d22b878c1d685476cf71167e7163be65416da68 --- .../modes/ZenModesListItemPreference.java | 45 +++++++++++++------ .../modes/ZenModesListItemPreferenceTest.java | 34 ++++++++++++++ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java index 0909c6f5f2e..4b1ffd095f0 100644 --- a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java +++ b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java @@ -15,6 +15,8 @@ */ package com.android.settings.notification.modes; +import static com.android.settings.Utils.createAccessibleSequence; + import android.app.settings.SettingsEnums; import android.content.Context; import android.widget.TextView; @@ -28,6 +30,7 @@ import com.android.settingslib.RestrictedPreference; import com.android.settingslib.Utils; import com.android.settingslib.notification.modes.ZenIconLoader; import com.android.settingslib.notification.modes.ZenMode; +import com.android.settingslib.notification.modes.ZenModeDescriptions; import com.google.common.base.Strings; @@ -42,6 +45,7 @@ class ZenModesListItemPreference extends RestrictedPreference { private final Context mContext; private final ZenIconLoader mIconLoader; private final Executor mUiExecutor; + private final ZenModeDescriptions mDescriptions; private ZenMode mZenMode; private TextView mTitleView; @@ -58,6 +62,7 @@ class ZenModesListItemPreference extends RestrictedPreference { mContext = context; mIconLoader = iconLoader; mUiExecutor = uiExecutor; + mDescriptions = new ZenModeDescriptions(context); setZenMode(zenMode); setKey(zenMode.getId()); } @@ -89,20 +94,17 @@ class ZenModesListItemPreference extends RestrictedPreference { } setTitle(mZenMode.getName()); - String dynamicDescription = zenMode.getDynamicDescription(mContext); - CharSequence statusText = switch (mZenMode.getStatus()) { - case ENABLED_AND_ACTIVE -> - Strings.isNullOrEmpty(dynamicDescription) - ? mContext.getString(R.string.zen_mode_active_text) - : mContext.getString( - R.string.zen_mode_format_status_and_trigger, - mContext.getString(R.string.zen_mode_active_text), - dynamicDescription); - case ENABLED -> dynamicDescription; - case DISABLED_BY_USER -> mContext.getString(R.string.zen_mode_disabled_by_user); - case DISABLED_BY_OTHER -> mContext.getString(R.string.zen_mode_disabled_needs_setup); - }; - setSummary(statusText); + ZenMode.Status status = zenMode.getStatus(); + String statusText = getStatusText(status, mDescriptions.getTriggerDescription(zenMode)); + String triggerDescriptionForA11y = mDescriptions.getTriggerDescriptionForAccessibility( + zenMode); + + if (triggerDescriptionForA11y != null) { + setSummary(createAccessibleSequence(statusText, + getStatusText(status, triggerDescriptionForA11y))); + } else { + setSummary(statusText); + } setIconSize(ICON_SIZE_SMALL); FutureUtil.whenDone( @@ -116,6 +118,21 @@ class ZenModesListItemPreference extends RestrictedPreference { updateTextColor(zenMode); } + private String getStatusText(ZenMode.Status status, String triggerDescription) { + return switch (status) { + case ENABLED_AND_ACTIVE -> + Strings.isNullOrEmpty(triggerDescription) + ? mContext.getString(R.string.zen_mode_active_text) + : mContext.getString( + R.string.zen_mode_format_status_and_trigger, + mContext.getString(R.string.zen_mode_active_text), + triggerDescription); + case ENABLED -> Strings.nullToEmpty(triggerDescription); + case DISABLED_BY_USER -> mContext.getString(R.string.zen_mode_disabled_by_user); + case DISABLED_BY_OTHER -> mContext.getString(R.string.zen_mode_disabled_needs_setup); + }; + } + private void updateTextColor(@Nullable ZenMode zenMode) { boolean isActive = zenMode != null && zenMode.isActive(); if (mTitleView != null) { diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java index 3722e416061..4f96bf6da7a 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java @@ -16,12 +16,19 @@ package com.android.settings.notification.modes; +import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; +import static android.service.notification.SystemZenRules.PACKAGE_ANDROID; + import static com.google.common.truth.Truth.assertThat; import android.app.Flags; import android.content.Context; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; +import android.service.notification.SystemZenRules; +import android.service.notification.ZenModeConfig; +import android.text.Spanned; +import android.text.style.TtsSpan; import com.android.settingslib.notification.modes.TestModeBuilder; import com.android.settingslib.notification.modes.ZenIconLoader; @@ -37,6 +44,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Calendar; + @RunWith(RobolectricTestRunner.class) @EnableFlags(Flags.FLAG_MODES_UI) public class ZenModesListItemPreferenceTest { @@ -122,6 +131,31 @@ public class ZenModesListItemPreferenceTest { assertThat(preference.getIcon()).isNotNull(); } + @Test + public void setZenMode_scheduleTime_hasCustomTtsInSummary() { + ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo(); + scheduleInfo.days = new int[] { Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY }; + scheduleInfo.startHour = 11; + scheduleInfo.endHour = 15; + ZenMode mode = new TestModeBuilder() + .setPackage(PACKAGE_ANDROID) + .setType(TYPE_SCHEDULE_TIME) + .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo)) + .setTriggerDescription( + SystemZenRules.getTriggerDescriptionForScheduleTime(mContext, scheduleInfo)) + .build(); + + ZenModesListItemPreference preference = newPreference(mode); + + assertThat(preference.getSummary()).isInstanceOf(Spanned.class); + Spanned summary = (Spanned) preference.getSummary(); + TtsSpan[] ttsSpans = summary.getSpans(0, summary.length(), TtsSpan.class); + assertThat(ttsSpans).hasLength(1); + assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_TEXT); + assertThat(ttsSpans[0].getArgs().getString(TtsSpan.ARG_TEXT)).isEqualTo( + "Monday to Wednesday, 11:00 AM - 3:00 PM"); + } + private ZenModesListItemPreference newPreference(ZenMode zenMode) { return new ZenModesListItemPreference(mContext, mIconLoader, MoreExecutors.directExecutor(), zenMode); From 103ce4efa23074261e5109f66ae1eb8f692b3ff5 Mon Sep 17 00:00:00 2001 From: chelseahao Date: Mon, 11 Nov 2024 13:07:25 +0800 Subject: [PATCH 03/13] Enable audio sharing hysteresis mode fix when preview is on. Test: atest Bug: 368401233 Flag: com.android.settingslib.flags.audio_sharing_developer_option Change-Id: I3dad42011aadcfd1a2670d297c8e000d1b92324a --- .../audiosharing/AudioSharingUtils.java | 20 +++++++------- .../AudioStreamButtonController.java | 7 +++-- .../AudioStreamHeaderController.java | 17 ++++++------ .../audiostreams/AudioStreamStateHandler.java | 5 ++-- .../AudioStreamsCategoryController.java | 8 ------ .../audiostreams/AudioStreamsHelper.java | 7 ++--- .../AudioStreamsProgressCategoryCallback.java | 10 +++++-- ...udioStreamsProgressCategoryController.java | 16 +++++++----- .../AudioStreamButtonControllerTest.java | 14 ++++++++++ .../AudioStreamHeaderControllerTest.java | 14 ++++++++++ .../AudioStreamStateHandlerTest.java | 19 ++++++++++++++ .../AudioStreamsCategoryControllerTest.java | 26 +++++++++---------- .../audiostreams/AudioStreamsHelperTest.java | 15 +++++++++++ ...ioStreamsProgressCategoryCallbackTest.java | 26 +++++++++++++++++-- ...StreamsProgressCategoryControllerTest.java | 13 ++++++++++ 15 files changed, 157 insertions(+), 60 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java index 592c8eb84c5..5a15b6ae51c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java @@ -46,7 +46,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.bluetooth.VolumeControlProfile; -import com.android.settingslib.flags.Flags; import java.util.ArrayList; import java.util.Comparator; @@ -222,8 +221,8 @@ public class AudioSharingUtils { Log.d(TAG, "hasActiveConnectedLeadDevice return false due to null device manager."); return false; } - return deviceManager.getCachedDevicesCopy().stream().anyMatch( - BluetoothUtils::isActiveMediaDevice); + return deviceManager.getCachedDevicesCopy().stream() + .anyMatch(BluetoothUtils::isActiveMediaDevice); } /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */ @@ -348,17 +347,18 @@ public class AudioSharingUtils { } /** Set {@link CachedBluetoothDevice} as primary device for call audio */ - public static void setPrimary(@NonNull Context context, - @Nullable CachedBluetoothDevice cachedDevice) { + public static void setPrimary( + @NonNull Context context, @Nullable CachedBluetoothDevice cachedDevice) { if (cachedDevice == null) return; cachedDevice.setActive(); - if (Flags.audioSharingHysteresisModeFix()) { + if (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(context)) { int groupId = BluetoothUtils.getGroupId(cachedDevice); // TODO: use real key name in SettingsProvider - int userPreferredId = Settings.Secure.getInt( - context.getContentResolver(), - BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID, - BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + int userPreferredId = + Settings.Secure.getInt( + context.getContentResolver(), + BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID, + BluetoothCsipSetCoordinator.GROUP_ID_INVALID); if (groupId != userPreferredId) { Settings.Secure.putInt( context.getContentResolver(), diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonController.java index 48acf3256d0..f9cce4ce099 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonController.java @@ -16,8 +16,6 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; -import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix; - import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; @@ -38,6 +36,7 @@ import com.android.settings.R; import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; @@ -77,7 +76,7 @@ public class AudioStreamButtonController extends BasePreferenceController BluetoothLeBroadcastReceiveState state) { super.onReceiveStateChanged(sink, sourceId, state); boolean shouldUpdateButton = - audioSharingHysteresisModeFix() + BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext) ? AudioStreamsHelper.hasSourcePresent(state) : AudioStreamsHelper.isConnected(state); if (shouldUpdateButton) { @@ -157,7 +156,7 @@ public class AudioStreamButtonController extends BasePreferenceController } List sources = - audioSharingHysteresisModeFix() + BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext) ? mAudioStreamsHelper.getAllPresentSources() : mAudioStreamsHelper.getAllConnectedSources(); boolean isConnected = diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderController.java index 0ee93e7742e..88efff2b6ab 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderController.java @@ -37,6 +37,7 @@ import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.widget.EntityHeaderController; +import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.LayoutPreference; @@ -86,7 +87,7 @@ public class AudioStreamHeaderController extends BasePreferenceController updateSummary(); mAudioStreamsHelper.startMediaService( mContext, mBroadcastId, mBroadcastName); - } else if (audioSharingHysteresisModeFix() + } else if (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext) && AudioStreamsHelper.hasSourcePresent(state)) { // if source present but not connected, only update the summary updateSummary(); @@ -171,13 +172,13 @@ public class AudioStreamHeaderController extends BasePreferenceController : mContext.getString( AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY)) : mAudioStreamsHelper.getAllConnectedSources().stream() - .map( - BluetoothLeBroadcastReceiveState - ::getBroadcastId) - .anyMatch( - connectedBroadcastId -> - connectedBroadcastId - == mBroadcastId) + .map( + BluetoothLeBroadcastReceiveState + ::getBroadcastId) + .anyMatch( + connectedBroadcastId -> + connectedBroadcastId + == mBroadcastId) ? mContext.getString( AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY) : AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY; diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java index 458cfab55ff..563af74f57c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java @@ -18,8 +18,6 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE; -import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix; - import android.os.Handler; import android.os.Looper; import android.text.SpannableString; @@ -98,7 +96,8 @@ class AudioStreamStateHandler { newState == AudioStreamsProgressCategoryController .AudioStreamState.SOURCE_ADDED - || (audioSharingHysteresisModeFix() + || (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable( + preference.getContext()) && newState == AudioStreamsProgressCategoryController .AudioStreamState.SOURCE_PRESENT)); diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java index 0107c6ee49b..f45b63c76f5 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java @@ -31,7 +31,6 @@ import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.flags.Flags; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -80,13 +79,6 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo } } - @Override - public int getAvailabilityStatus() { - return Flags.enableLeAudioQrCodePrivateBroadcastSharing() - ? AVAILABLE - : UNSUPPORTED_ON_DEVICE; - } - @Override public void updateVisibility() { if (mPreference == null) return; diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java index 7c1281f7ac5..25a9135701b 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java @@ -19,7 +19,6 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID; import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_TITLE; import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES; -import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix; import static java.util.Collections.emptyList; @@ -271,7 +270,8 @@ public class AudioStreamsHelper { List sourceList = assistant.getAllSources(cachedDevice.getDevice()); if (!sourceList.isEmpty() - && (audioSharingHysteresisModeFix() + && (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable( + localBtManager.getContext()) || sourceList.stream().anyMatch(AudioStreamsHelper::isConnected))) { Log.d( TAG, @@ -284,7 +284,8 @@ public class AudioStreamsHelper { List list = assistant.getAllSources(device.getDevice()); if (!list.isEmpty() - && (audioSharingHysteresisModeFix() + && (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable( + localBtManager.getContext()) || list.stream().anyMatch(AudioStreamsHelper::isConnected))) { Log.d( TAG, diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java index b379d4e7314..f0034316372 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java @@ -16,19 +16,24 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; -import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.content.Context; + +import com.android.settingslib.bluetooth.BluetoothUtils; public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastAssistantCallback { private static final String TAG = "AudioStreamsProgressCategoryCallback"; + private final Context mContext; private final AudioStreamsProgressCategoryController mCategoryController; public AudioStreamsProgressCategoryCallback( + Context context, AudioStreamsProgressCategoryController audioStreamsProgressCategoryController) { + mContext = context; mCategoryController = audioStreamsProgressCategoryController; } @@ -41,7 +46,8 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA mCategoryController.handleSourceConnected(state); } else if (AudioStreamsHelper.isBadCode(state)) { mCategoryController.handleSourceConnectBadCode(state); - } else if (audioSharingHysteresisModeFix() && AudioStreamsHelper.hasSourcePresent(state)) { + } else if (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext) + && AudioStreamsHelper.hasSourcePresent(state)) { // Keep this check as the last, source might also present in above states mCategoryController.handleSourcePresent(state); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java index 7ab588260d0..f0a0c5b8f7f 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java @@ -16,8 +16,6 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; -import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix; - import static java.util.Collections.emptyList; import android.app.AlertDialog; @@ -101,7 +99,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro (p.getAudioStreamState() == AudioStreamsProgressCategoryController .AudioStreamState.SOURCE_ADDED - || (audioSharingHysteresisModeFix() + || (isAudioSharingHysteresisModeFixAvailable(mContext) && p.getAudioStreamState() == AudioStreamsProgressCategoryController .AudioStreamState @@ -147,7 +145,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro mAudioStreamsHelper = new AudioStreamsHelper(mBluetoothManager); mMediaControlHelper = new MediaControlHelper(mContext, mBluetoothManager); mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant(); - mBroadcastAssistantCallback = new AudioStreamsProgressCategoryCallback(this); + mBroadcastAssistantCallback = new AudioStreamsProgressCategoryCallback(context, this); } @Override @@ -258,7 +256,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro // change it's state. existingPreference.setAudioStreamMetadata(source); if (fromState != AudioStreamState.SOURCE_ADDED - && (!audioSharingHysteresisModeFix() + && (!isAudioSharingHysteresisModeFixAvailable(mContext) || fromState != AudioStreamState.SOURCE_PRESENT)) { Log.w( TAG, @@ -364,7 +362,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro // not, means the source is removed from the sink, we move back the preference to SYNCED // state. if ((preference.getAudioStreamState() == AudioStreamState.SOURCE_ADDED - || (audioSharingHysteresisModeFix() + || (isAudioSharingHysteresisModeFixAvailable(mContext) && preference.getAudioStreamState() == AudioStreamState.SOURCE_PRESENT)) && mAudioStreamsHelper.getAllConnectedSources().stream() @@ -600,7 +598,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro // Handle QR code scan, display currently connected streams then start scanning // sequentially handleSourceFromQrCodeIfExists(); - if (audioSharingHysteresisModeFix()) { + if (isAudioSharingHysteresisModeFixAvailable(mContext)) { // With hysteresis mode, we prioritize showing connected sources first. // If no connected sources are found, we then show present sources. List sources = @@ -702,4 +700,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro dialog.dismiss(); }); } + + private static boolean isAudioSharingHysteresisModeFixAvailable(Context context) { + return BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(context); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java index 1d39bc9f0db..4c25c11db70 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java @@ -17,6 +17,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX; +import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; import static com.google.common.truth.Truth.assertThat; @@ -31,10 +32,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; import android.view.View; @@ -46,6 +49,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -62,6 +66,7 @@ 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; import java.util.ArrayList; import java.util.Collections; @@ -73,6 +78,7 @@ import java.util.concurrent.Executor; shadows = { ShadowThreadUtils.class, ShadowAudioStreamsHelper.class, + ShadowBluetoothAdapter.class, }) public class AudioStreamButtonControllerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -95,6 +101,13 @@ public class AudioStreamButtonControllerTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract( + BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper); when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant); mFeatureFactory = FakeFeatureFactory.setupForTest(); @@ -261,6 +274,7 @@ public class AudioStreamButtonControllerTest { @Test public void testCallback_onReceiveStateChangedWithSourcePresent_updateButton() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); String address = "11:22:33:44:55:66"; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java index 5cdc7974846..54b26ec228a 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java @@ -20,6 +20,7 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY; import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY; import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX; +import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -28,9 +29,11 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.graphics.drawable.Drawable; import android.platform.test.flag.junit.SetFlagsRule; @@ -41,6 +44,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowEntityHeaderController; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; @@ -57,6 +61,7 @@ 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; import java.util.ArrayList; import java.util.Collections; @@ -69,6 +74,7 @@ import java.util.concurrent.Executor; ShadowEntityHeaderController.class, ShadowThreadUtils.class, ShadowAudioStreamsHelper.class, + ShadowBluetoothAdapter.class, }) public class AudioStreamHeaderControllerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -93,6 +99,13 @@ public class AudioStreamHeaderControllerTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract( + BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); ShadowEntityHeaderController.setUseMock(mHeaderController); ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper); @@ -260,6 +273,7 @@ public class AudioStreamHeaderControllerTest { @Test public void testCallback_onReceiveStateChangedWithSourcePresent_updateButton() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); String address = "11:22:33:44:55:66"; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java index bb873d44575..7b53ca6c74c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java @@ -17,6 +17,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX; +import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; import static com.google.common.truth.Truth.assertThat; @@ -31,6 +32,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; import android.text.SpannableString; @@ -38,6 +41,8 @@ import android.text.SpannableString; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -47,8 +52,14 @@ import org.mockito.Mock; 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 AudioStreamStateHandlerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -64,6 +75,13 @@ public class AudioStreamStateHandlerTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract( + BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); mHandler = spy(new AudioStreamStateHandler()); } @@ -109,6 +127,7 @@ public class AudioStreamStateHandlerTest { @Test public void testHandleStateChange_setNewState_sourcePresent() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); when(mHandler.getStateEnum()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java index 0e003097a3f..c83c8a2137c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java @@ -135,47 +135,47 @@ public class AudioStreamsCategoryControllerTest { @Test public void getAvailabilityStatus_flagOn() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } @Test public void getAvailabilityStatus_flagOff() { - mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); } @Test public void onStart_flagOff_doNothing() { - mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStart(mLifecycleOwner); verify(mBluetoothEventManager, never()).registerCallback(any()); } @Test public void onStart_flagOn_registerCallback() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStart(mLifecycleOwner); verify(mBluetoothEventManager).registerCallback(any()); } @Test public void onStop_flagOff_doNothing() { - mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStop(mLifecycleOwner); verify(mBluetoothEventManager, never()).unregisterCallback(any()); } @Test public void onStop_flagOn_unregisterCallback() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStop(mLifecycleOwner); verify(mBluetoothEventManager).unregisterCallback(any()); } @Test public void updateVisibility_flagOff_invisible() { - mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.updateVisibility(); shadowOf(Looper.getMainLooper()).idle(); assertThat(mPreference.isVisible()).isFalse(); @@ -183,7 +183,7 @@ public class AudioStreamsCategoryControllerTest { @Test public void updateVisibility_noConnectedLe_invisible() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.updateVisibility(); shadowOf(Looper.getMainLooper()).idle(); assertThat(mPreference.isVisible()).isFalse(); @@ -191,7 +191,7 @@ public class AudioStreamsCategoryControllerTest { @Test public void updateVisibility_isNotProfileReady_invisible() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected( mCachedBluetoothDevice); when(mVolumeControl.isProfileReady()).thenReturn(false); @@ -202,7 +202,7 @@ public class AudioStreamsCategoryControllerTest { @Test public void updateVisibility_isBroadcasting_invisible() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected( mCachedBluetoothDevice); when(mBroadcast.isEnabled(any())).thenReturn(true); @@ -213,7 +213,7 @@ public class AudioStreamsCategoryControllerTest { @Test public void updateVisibility_isBluetoothOff_invisible() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected( mCachedBluetoothDevice); mShadowBluetoothAdapter.setEnabled(false); @@ -224,7 +224,7 @@ public class AudioStreamsCategoryControllerTest { @Test public void updateVisibility_visible() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected( mCachedBluetoothDevice); mController.displayPreference(mScreen); @@ -235,7 +235,7 @@ public class AudioStreamsCategoryControllerTest { @Test public void onProfileConnectionStateChanged_updateVisibility() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(BluetoothCallback.class); mController.onStart(mLifecycleOwner); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelperTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelperTest.java index fca1137e5c7..abfc4b7cc80 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelperTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelperTest.java @@ -20,6 +20,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX; +import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; import static com.google.common.truth.Truth.assertThat; @@ -33,9 +34,11 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -45,6 +48,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; @@ -65,6 +69,7 @@ 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; import java.util.ArrayList; import java.util.Collections; @@ -74,6 +79,7 @@ import java.util.List; @Config( shadows = { ShadowThreadUtils.class, + ShadowBluetoothAdapter.class, }) public class AudioStreamsHelperTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -97,6 +103,13 @@ public class AudioStreamsHelperTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract( + BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()) @@ -229,6 +242,7 @@ public class AudioStreamsHelperTest { @Test public void getAllPresentSources_noSource() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); List devices = new ArrayList<>(); @@ -251,6 +265,7 @@ public class AudioStreamsHelperTest { @Test public void getAllPresentSources_returnSource() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); String address = "11:22:33:44:55:66"; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java index 1e645282227..4e962c7deb3 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java @@ -17,6 +17,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX; +import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -24,11 +25,18 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -37,15 +45,21 @@ import org.mockito.Mock; 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; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowBluetoothAdapter.class, + }) public class AudioStreamsProgressCategoryCallbackTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - + private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private AudioStreamsProgressCategoryController mController; @Mock private BluetoothDevice mDevice; @Mock private BluetoothLeBroadcastReceiveState mState; @@ -56,7 +70,14 @@ public class AudioStreamsProgressCategoryCallbackTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); - mCallback = new AudioStreamsProgressCategoryCallback(mController); + ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract( + BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mCallback = new AudioStreamsProgressCategoryCallback(mContext, mController); } @Test @@ -71,6 +92,7 @@ public class AudioStreamsProgressCategoryCallbackTest { @Test public void testOnReceiveStateChanged_sourcePresent() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); String address = "11:22:33:44:55:66"; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java index 227748ae232..78d4d6e1361 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java @@ -26,6 +26,7 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.UNSET_BROADCAST_ID; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX; +import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; import static com.google.common.truth.Truth.assertThat; @@ -48,6 +49,7 @@ import android.bluetooth.BluetoothLeAudioContentMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.os.Looper; import android.platform.test.flag.junit.SetFlagsRule; @@ -62,6 +64,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settingslib.bluetooth.BluetoothEventManager; @@ -84,6 +87,7 @@ 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; import org.robolectric.shadows.ShadowAlertDialog; import org.robolectric.shadows.androidx.fragment.FragmentController; @@ -97,6 +101,7 @@ import java.util.List; ShadowAudioStreamsHelper.class, ShadowThreadUtils.class, ShadowAlertDialog.class, + ShadowBluetoothAdapter.class, }) public class AudioStreamsProgressCategoryControllerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -129,6 +134,13 @@ public class AudioStreamsProgressCategoryControllerTest { @Before public void setUp() { + ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract( + BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper); when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant); when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList()); @@ -292,6 +304,7 @@ public class AudioStreamsProgressCategoryControllerTest { @Test public void testOnStart_initHasDevice_getPresentSources() { + mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); // Setup a device From 4e4ee7430cbfdbd054cee7ea4d8773c39d9cf65a Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Tue, 12 Nov 2024 17:08:16 +0800 Subject: [PATCH 04/13] Unify and merge two hasAllApns() Also add unit tests. Fix: 374226933 Flag: EXEMPT refactor Test: manual - on ApnSettings Test: atest ApnTypesTest Change-Id: Ie82252ed13d81118292214b360ac49de9a81b7cc --- .../settings/network/apn/ApnEditor.java | 104 ------------------ .../settings/network/apn/ApnSettings.java | 5 +- .../android/settings/network/apn/ApnStatus.kt | 3 +- .../android/settings/network/apn/ApnTypes.kt | 34 +++++- .../settings/network/apn/ApnTypesTest.kt | 62 +++++++++++ 5 files changed, 98 insertions(+), 110 deletions(-) diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java index 533fd292289..5ae280c37cc 100644 --- a/src/com/android/settings/network/apn/ApnEditor.java +++ b/src/com/android/settings/network/apn/ApnEditor.java @@ -23,88 +23,21 @@ import android.app.settings.SettingsEnums; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.PersistableBundle; import android.os.UserManager; import android.provider.Telephony; -import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.util.Log; import androidx.annotation.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.spa.SpaActivity; -import java.util.Arrays; -import java.util.List; - /** Use to edit apn settings. */ public class ApnEditor extends SettingsPreferenceFragment { private static final String TAG = ApnEditor.class.getSimpleName(); - /** - * APN types for data connections. These are usage categories for an APN - * entry. One APN entry may support multiple APN types, eg, a single APN - * may service regular internet traffic ("default") as well as MMS-specific - * connections.
- * APN_TYPE_ALL is a special type to indicate that this APN entry can - * service all data connections. - */ - public static final String APN_TYPE_ALL = "*"; - /** APN type for default data traffic */ - public static final String APN_TYPE_DEFAULT = "default"; - /** APN type for MMS traffic */ - public static final String APN_TYPE_MMS = "mms"; - /** APN type for SUPL assisted GPS */ - public static final String APN_TYPE_SUPL = "supl"; - /** APN type for DUN traffic */ - public static final String APN_TYPE_DUN = "dun"; - /** APN type for HiPri traffic */ - public static final String APN_TYPE_HIPRI = "hipri"; - /** APN type for FOTA */ - public static final String APN_TYPE_FOTA = "fota"; - /** APN type for IMS */ - public static final String APN_TYPE_IMS = "ims"; - /** APN type for CBS */ - public static final String APN_TYPE_CBS = "cbs"; - /** APN type for IA Initial Attach APN */ - public static final String APN_TYPE_IA = "ia"; - /** APN type for Emergency PDN. This is not an IA apn, but is used - * for access to carrier services in an emergency call situation. */ - public static final String APN_TYPE_EMERGENCY = "emergency"; - /** APN type for Mission Critical Services */ - public static final String APN_TYPE_MCX = "mcx"; - /** APN type for XCAP */ - public static final String APN_TYPE_XCAP = "xcap"; - /** APN type for OEM_PAID networks (Automotive PANS) */ - public static final String APN_TYPE_OEM_PAID = "oem_paid"; - /** APN type for OEM_PRIVATE networks (Automotive PANS) */ - public static final String APN_TYPE_OEM_PRIVATE = "oem_private"; - /** Array of all APN types */ - public static final String[] APN_TYPES = {APN_TYPE_DEFAULT, - APN_TYPE_MMS, - APN_TYPE_SUPL, - APN_TYPE_DUN, - APN_TYPE_HIPRI, - APN_TYPE_FOTA, - APN_TYPE_IMS, - APN_TYPE_CBS, - APN_TYPE_IA, - APN_TYPE_EMERGENCY, - APN_TYPE_MCX, - APN_TYPE_XCAP, - APN_TYPE_OEM_PAID, - APN_TYPE_OEM_PRIVATE, - }; - - /** Array of APN types that are never user-editable */ - private static final String[] ALWAYS_READ_ONLY_APN_TYPES = new String[] { - APN_TYPE_OEM_PAID, - APN_TYPE_OEM_PRIVATE, - }; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -143,43 +76,6 @@ public class ApnEditor extends SettingsPreferenceFragment { } } - /** - * Fetch complete list of read only APN types. - * - * The list primarily comes from carrier config, but is also supplied by APN types which are - * always read only. - */ - static String[] getReadOnlyApnTypes(PersistableBundle b) { - String[] carrierReadOnlyApnTypes = b.getStringArray( - CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY); - return ArrayUtils.concat(String.class, carrierReadOnlyApnTypes, ALWAYS_READ_ONLY_APN_TYPES); - } - - /** - * Check if passed in array of APN types indicates all APN types - * @param apnTypes array of APN types. "*" indicates all types. - * @return true if all apn types are included in the array, false otherwise - */ - static boolean hasAllApns(String[] apnTypes) { - if (ArrayUtils.isEmpty(apnTypes)) { - return false; - } - - final List apnList = Arrays.asList(apnTypes); - if (apnList.contains(APN_TYPE_ALL)) { - Log.d(TAG, "hasAllApns: true because apnList.contains(APN_TYPE_ALL)"); - return true; - } - for (String apn : APN_TYPES) { - if (!apnList.contains(apn)) { - return false; - } - } - - Log.d(TAG, "hasAllApns: true"); - return true; - } - @Override public int getMetricsCategory() { return SettingsEnums.APN_EDITOR; diff --git a/src/com/android/settings/network/apn/ApnSettings.java b/src/com/android/settings/network/apn/ApnSettings.java index 0e3c3a4362d..55de5db5029 100644 --- a/src/com/android/settings/network/apn/ApnSettings.java +++ b/src/com/android/settings/network/apn/ApnSettings.java @@ -59,6 +59,7 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import kotlin.Unit; import java.util.ArrayList; +import java.util.List; /** Handle each different apn setting. */ public class ApnSettings extends RestrictedSettingsFragment @@ -135,9 +136,9 @@ public class ApnSettings extends RestrictedSettingsFragment mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL); mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); if (mAllowAddingApns) { - final String[] readOnlyApnTypes = ApnEditor.getReadOnlyApnTypes(b); + final List readOnlyApnTypes = ApnTypes.getReadOnlyApnTypes(b); // if no apn type can be edited, do not allow adding APNs - if (ApnEditor.hasAllApns(readOnlyApnTypes)) { + if (ApnTypes.hasAllApnTypes(readOnlyApnTypes)) { Log.d(TAG, "not allowing adding APN because all APN types are read only"); mAllowAddingApns = false; } diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt index 68588bbd975..d33c8b3cc73 100644 --- a/src/com/android/settings/network/apn/ApnStatus.kt +++ b/src/com/android/settings/network/apn/ApnStatus.kt @@ -25,6 +25,7 @@ import android.telephony.CarrierConfigManager import android.util.Log import com.android.settings.R import com.android.settings.network.apn.ApnTypes.getPreSelectedApnType +import com.android.settings.network.apn.ApnTypes.getReadOnlyApnTypes private const val TAG = "ApnStatus" @@ -204,7 +205,7 @@ fun getCarrierCustomizedConfig( CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL ) val customizedConfig = CustomizedConfig( - readOnlyApnTypes = ApnEditor.getReadOnlyApnTypes(b)?.toList() ?: emptyList(), + readOnlyApnTypes = b.getReadOnlyApnTypes(), readOnlyApnFields = b.getStringArray( CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY )?.toList() ?: emptyList(), diff --git a/src/com/android/settings/network/apn/ApnTypes.kt b/src/com/android/settings/network/apn/ApnTypes.kt index 4f84ac7fa70..ee192b262b4 100644 --- a/src/com/android/settings/network/apn/ApnTypes.kt +++ b/src/com/android/settings/network/apn/ApnTypes.kt @@ -17,6 +17,8 @@ package com.android.settings.network.apn import android.content.Context +import android.os.PersistableBundle +import android.telephony.CarrierConfigManager import android.telephony.data.ApnSetting import android.util.Log import android.widget.Toast @@ -51,9 +53,7 @@ object ApnTypes { private fun splitToList(apnType: String): List { val types = apnType.split(',').map { it.trim().toLowerCase(Locale.current) } - if (ApnSetting.TYPE_ALL_STRING in types || APN_TYPES.all { it in types }) { - return listOf(ApnSetting.TYPE_ALL_STRING) - } + if (hasAllApnTypes(types)) return listOf(ApnSetting.TYPE_ALL_STRING) return APN_TYPES.filter { it in types } } @@ -132,4 +132,32 @@ object ApnTypes { private fun defaultPreSelectedApnTypes(readOnlyApnTypes: List) = if (ApnSetting.TYPE_ALL_STRING in readOnlyApnTypes) emptyList() else PreSelectedTypes.filterNot { it in readOnlyApnTypes } + + /** Array of APN types that are never user-editable */ + private val ALWAYS_READ_ONLY_APN_TYPES = + arrayOf(ApnSetting.TYPE_OEM_PAID_STRING, ApnSetting.TYPE_OEM_PRIVATE_STRING) + + /** + * Fetch complete list of read only APN types. + * + * The list primarily comes from carrier config, but is also supplied by APN types which are + * always read only. + */ + @JvmStatic + fun PersistableBundle.getReadOnlyApnTypes(): List { + val carrierReadOnlyApnTypes = + getStringArray(CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY)?.toList() + ?: emptyList() + return carrierReadOnlyApnTypes + ALWAYS_READ_ONLY_APN_TYPES + } + + /** + * Check if passed in array of APN types indicates all APN types + * + * @param apnTypes array of APN types. "*" indicates all types. + * @return true if all apn types are included in the array, false otherwise + */ + @JvmStatic + fun hasAllApnTypes(apnTypes: List): Boolean = + ApnSetting.TYPE_ALL_STRING in apnTypes || APN_TYPES.all { it in apnTypes } } diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt index ce0d0f55ffd..13b5167095c 100644 --- a/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt @@ -91,6 +91,68 @@ class ApnTypesTest { assertThat(apnType).isEqualTo("default,mms,supl,hipri,fota,cbs,xcap") } + @Test + fun hasAllApnTypes_allString() { + val apnTypes = listOf(ApnSetting.TYPE_ALL_STRING) + + val hasAllApnTypes = ApnTypes.hasAllApnTypes(apnTypes) + + assertThat(hasAllApnTypes).isTrue() + } + + @Test + fun hasAllApnTypes_allTypes() { + val apnTypes = listOf( + ApnSetting.TYPE_DEFAULT_STRING, + ApnSetting.TYPE_MMS_STRING, + ApnSetting.TYPE_SUPL_STRING, + ApnSetting.TYPE_DUN_STRING, + ApnSetting.TYPE_HIPRI_STRING, + ApnSetting.TYPE_FOTA_STRING, + ApnSetting.TYPE_IMS_STRING, + ApnSetting.TYPE_CBS_STRING, + ApnSetting.TYPE_IA_STRING, + ApnSetting.TYPE_EMERGENCY_STRING, + ApnSetting.TYPE_MCX_STRING, + ApnSetting.TYPE_XCAP_STRING, + ApnSetting.TYPE_VSIM_STRING, + ApnSetting.TYPE_BIP_STRING, + ApnSetting.TYPE_ENTERPRISE_STRING, + ApnSetting.TYPE_OEM_PAID_STRING, + ApnSetting.TYPE_OEM_PRIVATE_STRING, + ) + + val hasAllApnTypes = ApnTypes.hasAllApnTypes(apnTypes) + + assertThat(hasAllApnTypes).isTrue() + } + + @Test + fun hasAllApnTypes_allTypesExceptDefault() { + val apnTypes = listOf( + ApnSetting.TYPE_MMS_STRING, + ApnSetting.TYPE_SUPL_STRING, + ApnSetting.TYPE_DUN_STRING, + ApnSetting.TYPE_HIPRI_STRING, + ApnSetting.TYPE_FOTA_STRING, + ApnSetting.TYPE_IMS_STRING, + ApnSetting.TYPE_CBS_STRING, + ApnSetting.TYPE_IA_STRING, + ApnSetting.TYPE_EMERGENCY_STRING, + ApnSetting.TYPE_MCX_STRING, + ApnSetting.TYPE_XCAP_STRING, + ApnSetting.TYPE_VSIM_STRING, + ApnSetting.TYPE_BIP_STRING, + ApnSetting.TYPE_ENTERPRISE_STRING, + ApnSetting.TYPE_OEM_PAID_STRING, + ApnSetting.TYPE_OEM_PRIVATE_STRING, + ) + + val hasAllApnTypes = ApnTypes.hasAllApnTypes(apnTypes) + + assertThat(hasAllApnTypes).isFalse() + } + private companion object { const val APN_TYPE = "type" } From cb32a15f1bd19f5b23bed6febfe59282514537ce Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Tue, 12 Nov 2024 15:07:20 +0800 Subject: [PATCH 05/13] [Audiosharing] Fix hysteresis mode 1. Move start broadcast logic from onPlaybackStarted to onBroadcastMetadataChanged 2. Move add source logic from onReceiveStateChanged(BIS>0) to onSourceAdded Test: atest Bug: 355222285 Bug: 377658982 Flag: com.android.settingslib.flags.audio_sharing_hysteresis_mode_fix Change-Id: Ie0a2345e16caa2804fede930d174a67f2ec60259 --- ...oSharingCallAudioPreferenceController.java | 12 ++- ...udioSharingDevicePreferenceController.java | 28 +++---- ...dioSharingDeviceVolumeGroupController.java | 16 ++-- .../AudioSharingSwitchBarController.java | 84 +++++++++---------- ...ringCallAudioPreferenceControllerTest.java | 10 +-- ...SharingDevicePreferenceControllerTest.java | 27 +++--- ...haringDeviceVolumeGroupControllerTest.java | 24 +++--- .../AudioSharingSwitchBarControllerTest.java | 41 +++++---- 8 files changed, 110 insertions(+), 132 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java index 11a337fb616..719d6cb71e6 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java @@ -110,7 +110,10 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP @Override public void onSourceAdded( - @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @NonNull BluetoothDevice sink, int sourceId, int reason) { + Log.d(TAG, "onSourceAdded: updateSummary"); + updateSummary(); + } @Override public void onSourceAddFailed( @@ -138,12 +141,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP public void onReceiveStateChanged( @NonNull BluetoothDevice sink, int sourceId, - @NonNull BluetoothLeBroadcastReceiveState state) { - if (BluetoothUtils.isConnected(state)) { - Log.d(TAG, "onReceiveStateChanged: synced, updateSummary"); - updateSummary(); - } - } + @NonNull BluetoothLeBroadcastReceiveState state) {} }; public AudioSharingCallAudioPreferenceController(Context context) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java index 6069d6fac0b..c286ed6114e 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java @@ -116,7 +116,18 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro @Override public void onSourceAdded( - @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @NonNull BluetoothDevice sink, int sourceId, int reason) { + Log.d(TAG, "onSourceAdded: update sharing device list."); + if (mBluetoothDeviceUpdater != null) { + mBluetoothDeviceUpdater.forceUpdate(); + } + if (mDeviceManager != null && mDialogHandler != null) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(sink); + if (cachedDevice != null) { + mDialogHandler.closeOpeningDialogsForLeaDevice(cachedDevice); + } + } + } @Override public void onSourceAddFailed( @@ -173,20 +184,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro public void onReceiveStateChanged( @NonNull BluetoothDevice sink, int sourceId, - @NonNull BluetoothLeBroadcastReceiveState state) { - if (BluetoothUtils.isConnected(state)) { - Log.d(TAG, "onSourceAdded: update sharing device list."); - if (mBluetoothDeviceUpdater != null) { - mBluetoothDeviceUpdater.forceUpdate(); - } - if (mDeviceManager != null && mDialogHandler != null) { - CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(sink); - if (cachedDevice != null) { - mDialogHandler.closeOpeningDialogsForLeaDevice(cachedDevice); - } - } - } - } + @NonNull BluetoothLeBroadcastReceiveState state) {} }; public AudioSharingDevicePreferenceController(Context context) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java index 42de10a81b2..be0ee0b8b44 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java @@ -132,7 +132,12 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre @Override public void onSourceAdded( - @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @NonNull BluetoothDevice sink, int sourceId, int reason) { + Log.d(TAG, "onSourceAdded: update volume list."); + if (mBluetoothDeviceUpdater != null) { + mBluetoothDeviceUpdater.forceUpdate(); + } + } @Override public void onSourceAddFailed( @@ -165,14 +170,7 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre public void onReceiveStateChanged( @NonNull BluetoothDevice sink, int sourceId, - @NonNull BluetoothLeBroadcastReceiveState state) { - if (BluetoothUtils.isConnected(state)) { - Log.d(TAG, "onReceiveStateChanged: synced, update volume list."); - if (mBluetoothDeviceUpdater != null) { - mBluetoothDeviceUpdater.forceUpdate(); - } - } - } + @NonNull BluetoothLeBroadcastReceiveState state) {} }; public AudioSharingDeviceVolumeGroupController(Context context) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 537eee09af0..0a9bc97ed79 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -177,6 +177,20 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + broadcastId + ", metadata = " + metadata.getBroadcastName()); + if (mAssistant == null + || mAssistant.getAllConnectedDevices().stream() + .anyMatch( + device -> BluetoothUtils + .hasActiveLocalBroadcastSourceForBtDevice( + device, mBtManager))) { + Log.d( + TAG, + "Skip handleOnBroadcastReady: null assistant or " + + "sink has active local source."); + cleanUpStatesForStartSharing(); + return; + } + handleOnBroadcastReady(); } @Override @@ -221,20 +235,6 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + reason + ", broadcastId = " + broadcastId); - if (mAssistant == null - || mAssistant.getAllConnectedDevices().stream() - .anyMatch( - device -> BluetoothUtils - .hasActiveLocalBroadcastSourceForBtDevice( - device, mBtManager))) { - Log.d( - TAG, - "Skip handleOnBroadcastReady: null assistant or " - + "sink has active local source."); - cleanUpStatesForStartSharing(); - return; - } - handleOnBroadcastReady(); } @Override @@ -261,7 +261,30 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @Override public void onSourceAdded( - @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @NonNull BluetoothDevice sink, int sourceId, int reason) { + if (mSinksInAdding.contains(sink)) { + mSinksInAdding.remove(sink); + } + dismissProgressDialogIfNeeded(); + Log.d(TAG, "onSourceAdded(), sink = " + sink + ", remaining sinks = " + + mSinksInAdding); + if (mSinksToWaitFor.contains(sink)) { + mSinksToWaitFor.remove(sink); + if (mSinksToWaitFor.isEmpty()) { + // To avoid users advance to share then pair flow before the + // primary/active sinks successfully join the audio sharing, + // popup dialog till adding source complete for mSinksToWaitFor. + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.AUDIO_SHARING_SETTINGS, + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE, + /* userTriggered= */ false, + /* deviceCountInSharing= */ 1, + /* candidateDeviceCount= */ 0); + showAudioSharingDialog(eventData); + } + } + } @Override public void onSourceAddFailed( @@ -307,34 +330,9 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @NonNull BluetoothDevice sink, int sourceId, @NonNull BluetoothLeBroadcastReceiveState state) { - if (mStoppingSharing.get()) { - Log.d(TAG, "Skip onReceiveStateChanged, stopping broadcast"); - return; - } - if (BluetoothUtils.isConnected(state)) { - if (mSinksInAdding.contains(sink)) { - mSinksInAdding.remove(sink); - } - dismissProgressDialogIfNeeded(); - Log.d(TAG, "onReceiveStateChanged() connected, sink = " + sink - + ", remaining sinks = " + mSinksInAdding); - if (mSinksToWaitFor.contains(sink)) { - mSinksToWaitFor.remove(sink); - if (mSinksToWaitFor.isEmpty()) { - // To avoid users advance to share then pair flow before the - // primary/active sinks successfully join the audio sharing, - // popup dialog till adding source complete for mSinksToWaitFor. - Pair[] eventData = - AudioSharingUtils.buildAudioSharingDialogEventData( - SettingsEnums.AUDIO_SHARING_SETTINGS, - SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE, - /* userTriggered= */ false, - /* deviceCountInSharing= */ 1, - /* candidateDeviceCount= */ 0); - showAudioSharingDialog(eventData); - } - } - } + Log.d(TAG, + "onReceiveStateChanged(), sink = " + sink + ", sourceId = " + sourceId + + ", state = " + state); } }; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java index a575d5ab7d7..4dc4dca4ab7 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java @@ -527,12 +527,12 @@ public class AudioSharingCallAudioPreferenceControllerTest { shadowOf(Looper.getMainLooper()).idle(); assertThat(mPreference.getSummary().toString()).isEmpty(); - // onReceiveStateChanged will update summary + // onSourceAdded will update summary Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1)); when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); - mController.mBroadcastAssistantCallback.onReceiveStateChanged( - mDevice1, /* sourceId= */ 1, mState); + mController.mBroadcastAssistantCallback.onSourceAdded(mDevice1, /* sourceId= */ + 1, /* reason= */ 1); shadowOf(Looper.getMainLooper()).idle(); assertThat(mPreference.getSummary().toString()) .isEqualTo( @@ -557,8 +557,6 @@ public class AudioSharingCallAudioPreferenceControllerTest { mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); - mController.mBroadcastAssistantCallback.onSourceAdded( - mDevice1, /* sourceId= */ 1, /* reason= */ 1); mController.mBroadcastAssistantCallback.onSourceAddFailed( mDevice1, mSource, /* reason= */ 1); mController.mBroadcastAssistantCallback.onSourceRemoved( @@ -572,6 +570,8 @@ public class AudioSharingCallAudioPreferenceControllerTest { mController.mBroadcastAssistantCallback.onSourceFound(mSource); mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1); shadowOf(Looper.getMainLooper()).idle(); + mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1, + mState); // Above callbacks won't update summary. assertThat(mPreference.getSummary().toString()).isEmpty(); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java index 0bc0b949193..a49d0c13890 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java @@ -521,21 +521,9 @@ public class AudioSharingDevicePreferenceControllerTest { @Test public void testBluetoothLeBroadcastAssistantCallbacks_updateGroup() { - // onReceiveStateChanged with unconnected state will do nothing - when(mState.getBisSyncState()).thenReturn(new ArrayList<>()); - mController.mBroadcastAssistantCallback.onReceiveStateChanged( - mDevice, /* sourceId= */ 1, mState); - shadowOf(Looper.getMainLooper()).idle(); - verify(mBluetoothDeviceUpdater, never()).forceUpdate(); - verify(mDialogHandler, never()).closeOpeningDialogsForLeaDevice(mCachedDevice); - - // onReceiveStateChanged with connected state will update group preference and handle - // stale dialogs - List bisSyncState = new ArrayList<>(); - bisSyncState.add(1L); - when(mState.getBisSyncState()).thenReturn(bisSyncState); - mController.mBroadcastAssistantCallback.onReceiveStateChanged( - mDevice, /* sourceId= */ 1, mState); + // onSourceAdded will update group preference and handle stale dialogs + mController.mBroadcastAssistantCallback.onSourceAdded(mDevice, /* sourceId= */ + 1, /* reason= */ 1); shadowOf(Looper.getMainLooper()).idle(); verify(mBluetoothDeviceUpdater).forceUpdate(); verify(mDialogHandler).closeOpeningDialogsForLeaDevice(mCachedDevice); @@ -572,8 +560,13 @@ public class AudioSharingDevicePreferenceControllerTest { mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); - mController.mBroadcastAssistantCallback.onSourceAdded( - mDevice, /* sourceId= */ 1, /* reason= */ 1); + List bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mState.getBisSyncState()).thenReturn(bisSyncState); + when(mBroadcast.getLatestBroadcastId()).thenReturn(1); + when(mState.getBroadcastId()).thenReturn(1); + mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 1, + mState); mController.mBroadcastAssistantCallback.onSourceModified( mDevice, /* sourceId= */ 1, /* reason= */ 1); mController.mBroadcastAssistantCallback.onSourceModifyFailed( diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java index d8c663f6dc2..bac8b30ff16 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java @@ -461,18 +461,9 @@ public class AudioSharingDeviceVolumeGroupControllerTest { @Test public void testBluetoothLeBroadcastAssistantCallbacks_updateGroup() { - when(mState.getBisSyncState()).thenReturn(new ArrayList<>()); - // onReceiveStateChanged with unconnected state will do nothing - mController.mBroadcastAssistantCallback.onReceiveStateChanged( - mDevice1, /* sourceId= */ 1, mState); - verify(mDeviceUpdater, never()).forceUpdate(); - - // onReceiveStateChanged with connected state will update group preference - List bisSyncState = new ArrayList<>(); - bisSyncState.add(1L); - when(mState.getBisSyncState()).thenReturn(bisSyncState); - mController.mBroadcastAssistantCallback.onReceiveStateChanged( - mDevice1, /* sourceId= */ 1, mState); + // onSourceAdded will update group preference + mController.mBroadcastAssistantCallback.onSourceAdded(mDevice1, /* sourceId= */ + 1, /* reason= */ 1); verify(mDeviceUpdater).forceUpdate(); // onSourceRemoved will update group preference @@ -487,8 +478,13 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); - mController.mBroadcastAssistantCallback.onSourceAdded( - mDevice1, /* sourceId= */ 1, /* reason= */ 1); + List bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mState.getBisSyncState()).thenReturn(bisSyncState); + when(mBroadcast.getLatestBroadcastId()).thenReturn(1); + when(mState.getBroadcastId()).thenReturn(1); + mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1, + mState); mController.mBroadcastAssistantCallback.onSourceAddFailed( mDevice1, mSource, /* reason= */ 1); mController.mBroadcastAssistantCallback.onSourceRemoveFailed( diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java index fb5aa1c957e..7851b2b38f8 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java @@ -476,7 +476,7 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_notInit_noDialog() { + public void onBroadcastMetadataChanged_notInit_noDialog() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); @@ -503,7 +503,7 @@ public class AudioSharingSwitchBarControllerTest { // No progress dialog. assertThat(childFragments).isEmpty(); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mFeatureFactory.metricsFeatureProvider) @@ -515,7 +515,7 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_hasLocalSource_noDialog() { + public void onBroadcastMetadataChanged_hasLocalSource_noDialog() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); @@ -533,7 +533,7 @@ public class AudioSharingSwitchBarControllerTest { assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( AudioSharingProgressDialogFragment.class.getName()); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mAssistant, never()).addSource(any(), any(), anyBoolean()); @@ -549,7 +549,7 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_singleActiveDevice_showJoinAudioSharingDialog() { + public void onBroadcastMetadataChanged_singleActiveDevice_showJoinAudioSharingDialog() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); @@ -566,15 +566,15 @@ public class AudioSharingSwitchBarControllerTest { when(mBroadcast.isEnabled(null)).thenReturn(true); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mFeatureFactory.metricsFeatureProvider) .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING)); when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L)); - mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice2, /* sourceId= */ 1, - mState); + mController.mBroadcastAssistantCallback.onSourceAdded(mDevice2, /* sourceId= */ + 1, /* reason= */ 1); shadowOf(Looper.getMainLooper()).idle(); childFragments = mParentFragment.getChildFragmentManager().getFragments(); @@ -613,7 +613,7 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_oneActiveOnConnected_showJoinAudioSharingDialog() { + public void onBroadcastMetadataChanged_oneActiveOnConnected_showJoinAudioSharingDialog() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); @@ -635,7 +635,7 @@ public class AudioSharingSwitchBarControllerTest { when(mBroadcast.isEnabled(null)).thenReturn(true); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mFeatureFactory.metricsFeatureProvider) @@ -681,7 +681,7 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_oneActiveOnConnected_clickShareBtnOnDialog_addSource() { + public void onBroadcastMetadataChanged_oneActiveOnConnected_clickShareBtnOnDialog_addSource() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); @@ -694,7 +694,7 @@ public class AudioSharingSwitchBarControllerTest { verify(mBroadcast).startPrivateBroadcast(); when(mBroadcast.isEnabled(null)).thenReturn(true); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false); @@ -722,7 +722,7 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_oneActiveOnConnected_clickCancelBtnOnDialog_doNothing() { + public void onBroadcastMetadataChanged_oneActiveOnConnected_clickCancelBtnOnDialog_doNothing() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); @@ -735,7 +735,7 @@ public class AudioSharingSwitchBarControllerTest { verify(mBroadcast).startPrivateBroadcast(); when(mBroadcast.isEnabled(null)).thenReturn(true); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false); @@ -835,7 +835,7 @@ public class AudioSharingSwitchBarControllerTest { when(mBroadcast.isEnabled(null)).thenReturn(true); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false); @@ -868,16 +868,15 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void testAssistantCallbacks_onReceiveStateChanged_dismissProgressDialog() { + public void testAssistantCallbacks_onSourceAdded_dismissProgressDialog() { AudioSharingProgressDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1); shadowOf(Looper.getMainLooper()).idle(); List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( AudioSharingProgressDialogFragment.class.getName()); - when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L)); - mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1, - mState); + mController.mBroadcastAssistantCallback.onSourceAdded(mDevice1, /* sourceId= */ + 1, /* reason= */ 1); shadowOf(Looper.getMainLooper()).idle(); childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).isEmpty(); @@ -892,8 +891,6 @@ public class AudioSharingSwitchBarControllerTest { mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); - mController.mBroadcastAssistantCallback.onSourceAdded( - mDevice1, /* sourceId= */ 1, /* reason= */ 1); mController.mBroadcastAssistantCallback.onSourceRemoved( mDevice1, /* sourceId= */ 1, /* reason= */ 1); mController.mBroadcastAssistantCallback.onSourceRemoveFailed( @@ -1013,7 +1010,7 @@ public class AudioSharingSwitchBarControllerTest { shadowOf(Looper.getMainLooper()).idle(); verify(mBroadcast).startPrivateBroadcast(); - mController.mBroadcastCallback.onPlaybackStarted(0, 0); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); shadowOf(Looper.getMainLooper()).idle(); verify(mFeatureFactory.metricsFeatureProvider) From a37572a4e565a1a1a0a3ee3eb3e9236a9643a2e7 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 8 Nov 2024 13:32:00 -0500 Subject: [PATCH 06/13] reduce number of binder calls when loading page Test: ZenModeAllBypassingAppsPreferenceControllerTest Test: ZenModeAppsLinkPreferenceControllerTest Test: manual - load page and validate that it loads a few seconds faster Flag: EXEMPT bug fix Fixes: 368623163 Change-Id: I6d34a21f0948b117a96beefc405de4b623f49609 --- .../notification/modes/ZenHelperBackend.java | 19 +++++--- ...eAllBypassingAppsPreferenceController.java | 44 ++++++++++++------- .../ZenModeAppsLinkPreferenceController.java | 3 +- .../ZenModeSelectBypassingAppsFragment.java | 10 +++-- ...BypassingAppsPreferenceControllerTest.java | 26 ++++++----- ...nModeAppsLinkPreferenceControllerTest.java | 36 +++++++-------- 6 files changed, 80 insertions(+), 58 deletions(-) diff --git a/src/com/android/settings/notification/modes/ZenHelperBackend.java b/src/com/android/settings/notification/modes/ZenHelperBackend.java index 31c1ce40b19..5761d361e33 100644 --- a/src/com/android/settings/notification/modes/ZenHelperBackend.java +++ b/src/com/android/settings/notification/modes/ZenHelperBackend.java @@ -18,6 +18,7 @@ package com.android.settings.notification.modes; import android.annotation.Nullable; import android.app.INotificationManager; +import android.app.ZenBypassingApp; import android.content.ContentProvider; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -30,6 +31,7 @@ import android.os.UserManager; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.service.notification.ConversationChannelWrapper; +import android.util.ArrayMap; import android.util.Log; import androidx.annotation.NonNull; @@ -41,8 +43,9 @@ import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Function; /** @@ -76,16 +79,20 @@ class ZenHelperBackend { } /** - * Returns all of a user's packages that have at least one channel that will bypass DND + * Returns a mapping between a user's packages that have at least one channel that will + * bypass DND, and a Boolean indicating whether all of the package's channels bypass. */ - List getPackagesBypassingDnd(int userId, - boolean includeConversationChannels) { + Map getPackagesBypassingDnd(int userId) { + Map bypassingAppsMap = new HashMap<>(); try { - return mInm.getPackagesBypassingDnd(userId, includeConversationChannels); + List bypassingApps = mInm.getPackagesBypassingDnd(userId).getList(); + for (ZenBypassingApp zba : bypassingApps) { + bypassingAppsMap.put(zba.getPkg(), zba.doAllChannelsBypass()); + } } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); - return new ArrayList<>(); } + return bypassingAppsMap; } /** Returns all conversation channels for profiles of the current user. */ diff --git a/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java index 922ac5ecf5b..a01406ff836 100644 --- a/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceController.java @@ -22,7 +22,9 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; +import android.os.UserManager; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.text.BidiFormatter; @@ -35,7 +37,6 @@ import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.SubSettingLauncher; -import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.app.AppChannelsBypassingDndSettings; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; @@ -44,7 +45,9 @@ import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.AppPreference; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Adds a preference to the PreferenceScreen for each notification channel that can bypass DND. @@ -54,7 +57,8 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere public static final String KEY_NO_APPS = "all_none"; private static final String KEY = "zen_mode_bypassing_apps_list"; - @Nullable private final NotificationBackend mNotificationBackend; + @Nullable private final ZenHelperBackend mHelperBackend; + private final UserManager mUserManager; @Nullable @VisibleForTesting ApplicationsState mApplicationsState; @VisibleForTesting PreferenceCategory mPreferenceCategory; @@ -64,18 +68,18 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere @Nullable private Fragment mHostFragment; public ZenModeAllBypassingAppsPreferenceController(Context context, @Nullable Application app, - @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) { - this(context, app == null ? null : ApplicationsState.getInstance(app), host, - notificationBackend); + @Nullable Fragment host, @Nullable ZenHelperBackend helperBackend) { + this(context, app == null ? null : ApplicationsState.getInstance(app), host, helperBackend); } private ZenModeAllBypassingAppsPreferenceController(Context context, @Nullable ApplicationsState appState, @Nullable Fragment host, - @Nullable NotificationBackend notificationBackend) { + @Nullable ZenHelperBackend helperBackend) { super(context); - mNotificationBackend = notificationBackend; mApplicationsState = appState; mHostFragment = host; + mHelperBackend = helperBackend; + mUserManager = context.getSystemService(UserManager.class); if (mApplicationsState != null && host != null) { mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); @@ -140,19 +144,25 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere } boolean doAnyAppsPassCriteria = false; + Map> packagesBypassingDndByUser = new HashMap<>(); + for (UserHandle userHandle : mUserManager.getUserProfiles()) { + packagesBypassingDndByUser.put(userHandle.getIdentifier(), + mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier())); + } for (ApplicationsState.AppEntry app : apps) { String pkg = app.info.packageName; final String key = getKey(pkg, app.info.uid); - final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid); - final int appChannelsBypassingDnd = mNotificationBackend - .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size(); - if (appChannelsBypassingDnd > 0) { + boolean doesAppBypassDnd = false; + int userId = UserHandle.getUserId(app.info.uid); + Map packagesBypassingDnd = + packagesBypassingDndByUser.getOrDefault(userId, new HashMap<>()); + if (packagesBypassingDnd.containsKey(pkg)) { doAnyAppsPassCriteria = true; + doesAppBypassDnd = true; } - Preference pref = mPreferenceCategory.findPreference(key); if (pref == null) { - if (appChannelsBypassingDnd > 0) { + if (doesAppBypassDnd) { // does not exist but should pref = new AppPreference(mPrefContext); pref.setKey(key); @@ -172,14 +182,14 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere }); pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label)); updateIcon(pref, app); - if (appChannels > appChannelsBypassingDnd) { - pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some); - } else { + if (packagesBypassingDnd.get(pkg)) { pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all); + } else { + pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some); } mPreferenceCategory.addPreference(pref); } - } else if (appChannelsBypassingDnd == 0) { + } else if (!doesAppBypassDnd) { // exists but shouldn't anymore mPreferenceCategory.removePreference(pref); } diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java index c133f515f30..e0e36f6495f 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java @@ -161,8 +161,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr Multimap packagesBypassingDnd = HashMultimap.create(); for (UserHandle userHandle : mUserManager.getUserProfiles()) { packagesBypassingDnd.putAll(userHandle.getIdentifier(), - mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier(), - /* includeConversationChannels= */ false)); + mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier()).keySet()); } return ImmutableList.copyOf( diff --git a/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java b/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java index 1f5438d08eb..21f34a2005d 100644 --- a/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java @@ -48,15 +48,17 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl } else { app = null; } - return buildPreferenceControllers(context, app, this, new NotificationBackend()); + return buildPreferenceControllers(context, app, this, new NotificationBackend(), + new ZenHelperBackend(context)); } private static List buildPreferenceControllers(Context context, @Nullable Application app, @Nullable Fragment host, - @Nullable NotificationBackend notificationBackend) { + @Nullable NotificationBackend notificationBackend, + @Nullable ZenHelperBackend zenHelperBackend) { final List controllers = new ArrayList<>(); controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host, - notificationBackend)); + zenHelperBackend)); controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host, notificationBackend)); return controllers; @@ -86,7 +88,7 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl @Override public List createPreferenceControllers( Context context) { - return buildPreferenceControllers(context, null, null, null); + return buildPreferenceControllers(context, null, null, null, null); } }; } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java index a7d52b16575..f9601d70f6c 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAllBypassingAppsPreferenceControllerTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -28,10 +27,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Flags; -import android.app.NotificationChannel; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.ParceledListSlice; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -39,7 +36,6 @@ import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; -import com.android.settings.notification.NotificationBackend; import com.android.settingslib.applications.ApplicationsState; import org.junit.Before; @@ -54,6 +50,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.List; +import java.util.Map; @RunWith(RobolectricTestRunner.class) @EnableFlags(Flags.FLAG_MODES_UI) @@ -66,7 +63,7 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest { private Context mContext; @Mock - private NotificationBackend mBackend; + private ZenHelperBackend mBackend; @Mock private PreferenceCategory mPreferenceCategory; @Mock @@ -102,18 +99,25 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest { entry2.info.packageName = "test2"; entry2.info.uid = 0; + ApplicationsState.AppEntry entry3= mock(ApplicationsState.AppEntry.class); + entry3.info = new ApplicationInfo(); + entry3.info.packageName = "test3"; + entry3.info.uid = 0; + List appEntries = new ArrayList<>(); appEntries.add(entry1); appEntries.add(entry2); - List channelsBypassing = new ArrayList<>(); - channelsBypassing.add(mock(NotificationChannel.class)); - channelsBypassing.add(mock(NotificationChannel.class)); - when(mBackend.getNotificationChannelsBypassingDnd(anyString(), - anyInt())).thenReturn(new ParceledListSlice<>(channelsBypassing)); + appEntries.add(entry3); + when(mBackend.getPackagesBypassingDnd(anyInt())).thenReturn( + Map.of("test", true, "test2", false)); // THEN there's are two preferences mController.updateAppList(appEntries); - verify(mPreferenceCategory, times(2)).addPreference(any()); + ArgumentCaptor captor = ArgumentCaptor.forClass(Preference.class); + verify(mPreferenceCategory, times(2)).addPreference(captor.capture()); + List prefs = captor.getAllValues(); + assertThat(prefs.get(0).getSummary().toString()).isEqualTo("All notifications"); + assertThat(prefs.get(1).getSummary().toString()).isEqualTo("Some notifications"); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java index fa83f309db1..e747b423af5 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java @@ -77,6 +77,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Random; @RunWith(RobolectricTestRunner.class) @@ -200,8 +201,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest { ApplicationsState.AppEntry app2 = createAppEntry("app2", mContext.getUserId()); List allApps = List.of(app1, app2); - when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), - false)).thenReturn(List.of("app1")); + when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId())).thenReturn( + Map.of("app1", true)); assertThat(mController.getAppsBypassingDndSortedByName(allApps)).containsExactly(app1); } @@ -213,8 +214,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest { ApplicationsState.AppEntry appB = createAppEntry("B", mContext.getUserId()); List allApps = List.of(appC, appA, appB); - when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()), anyBoolean())) - .thenReturn(List.of("B", "C", "A")); + when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()))) + .thenReturn(Map.of("B", true, "C", false, "A", true)); assertThat(mController.getAppsBypassingDndSortedByName(allApps)) .containsExactly(appA, appB, appC).inOrder(); @@ -234,10 +235,10 @@ public final class ZenModeAppsLinkPreferenceControllerTest { List allApps = List.of(workCopy, personalCopy, otherPersonal, otherWork); - when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()), anyBoolean())) - .thenReturn(List.of("app", "p2")); - when(mHelperBackend.getPackagesBypassingDnd(eq(10), anyBoolean())) - .thenReturn(List.of("app")); + when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()))) + .thenReturn(Map.of("app", true, "p2", true)); + when(mHelperBackend.getPackagesBypassingDnd(eq(10))) + .thenReturn(Map.of("app", false)); // Personal copy before work copy (names match). assertThat(mController.getAppsBypassingDndSortedByName(allApps)) @@ -253,7 +254,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest { mController.updateState(mPreference, zenMode); verifyNoMoreInteractions(mSession); - verify(mHelperBackend, never()).getPackagesBypassingDnd(anyInt(), anyBoolean()); + verify(mHelperBackend, never()).getPackagesBypassingDnd(anyInt()); assertThat(String.valueOf(mPreference.getSummary())).isEqualTo("None"); } @@ -266,9 +267,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest { ArrayList appEntries = new ArrayList<>(); appEntries.add(createAppEntry("test", mContext.getUserId())); - when(mHelperBackend.getPackagesBypassingDnd( - mContext.getUserId(), false)) - .thenReturn(List.of("test")); + when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId())) + .thenReturn(Map.of("test", false)); // Updates the preference with the zen mode. We expect that this causes the app session // to trigger a rebuild (and display a temporary text in the meantime). @@ -286,8 +286,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest { ZenMode zenMode = createPriorityChannelsZenMode(); mController.updateState(mPreference, zenMode); - when(mHelperBackend.getPackagesBypassingDnd(anyInt(), anyBoolean())) - .thenReturn(ImmutableList.of("test1", "test2")); + when(mHelperBackend.getPackagesBypassingDnd(anyInt())) + .thenReturn(Map.of("test1", false, "test2", false)); ArrayList appEntries = new ArrayList<>(); appEntries.add(createAppEntry("test1", mContext.getUserId())); appEntries.add(createAppEntry("test2", mContext.getUserId())); @@ -328,8 +328,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest { .build(); ArrayList appEntries = new ArrayList<>(); appEntries.add(createAppEntry("test", mContext.getUserId())); - when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), false)) - .thenReturn(List.of("test")); + when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId())) + .thenReturn(Map.of("test", true)); mController.updateState(mPreference, zenModeWithNone); @@ -355,8 +355,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest { .build(); ArrayList appEntries = new ArrayList<>(); appEntries.add(createAppEntry("test", mContext.getUserId())); - when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), false)) - .thenReturn(List.of("test")); + when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId())) + .thenReturn(Map.of("test", true)); mController.updateState(mPreference, zenModeWithPriority); From ff0ea037bf7fd82ace6219395f2f02dba6c4e674 Mon Sep 17 00:00:00 2001 From: Guojing Yuan Date: Tue, 12 Nov 2024 18:40:43 +0000 Subject: [PATCH 07/13] Remove the margin in Device Detail page Change-Id: I1add40d94bf1a433d303866353e08a3d790db1ae Fix: 377517620 Test: manual Flag: EXEMPT bugfix --- .../bluetooth/BluetoothDetailsCompanionAppsController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java index 93e1ff54574..b741f0833f0 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java @@ -87,6 +87,12 @@ public class BluetoothDetailsCompanionAppsController extends BluetoothDetailsCon mProfilesContainer.setLayoutResource(R.layout.preference_companion_app); } + @Override + public boolean isAvailable() { + // Do not show this preference for now. More details in b/191992001#comment7 + return false; + } + private List getAssociations(String address) { return filter( mCompanionDeviceManager.getAllAssociations(), From d6c932cd7e3a1f2ae5deb1c316973d8f739244ee Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 12 Nov 2024 15:25:21 -0500 Subject: [PATCH 08/13] Handle null audio attributes Test: SoundPreferenceControllerTest Fixes: 358540739 Flag: EXEMPT bugfix Change-Id: I9a0e6066ccc2315b276564178c13ed59dd22162b --- .../app/SoundPreferenceController.java | 2 ++ .../app/SoundPreferenceControllerTest.java | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/com/android/settings/notification/app/SoundPreferenceController.java b/src/com/android/settings/notification/app/SoundPreferenceController.java index b23b4fc86cb..bc1cb24ce1b 100644 --- a/src/com/android/settings/notification/app/SoundPreferenceController.java +++ b/src/com/android/settings/notification/app/SoundPreferenceController.java @@ -101,6 +101,8 @@ public class SoundPreferenceController extends NotificationPreferenceController public boolean handlePreferenceTreeClick(Preference preference) { if (KEY_SOUND.equals(preference.getKey()) && mFragment != null) { NotificationSoundPreference pref = (NotificationSoundPreference) preference; + // default to notification + pref.setRingtoneType(RingtoneManager.TYPE_NOTIFICATION); if (mChannel != null && mChannel.getAudioAttributes() != null) { if (USAGE_ALARM == mChannel.getAudioAttributes().getUsage()) { pref.setRingtoneType(RingtoneManager.TYPE_ALARM); diff --git a/tests/robotests/src/com/android/settings/notification/app/SoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/SoundPreferenceControllerTest.java index 273bcdbe213..6ec3eb2bec0 100644 --- a/tests/robotests/src/com/android/settings/notification/app/SoundPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/SoundPreferenceControllerTest.java @@ -302,6 +302,26 @@ public class SoundPreferenceControllerTest { RingtoneManager.EXTRA_RINGTONE_TYPE, 0)); } + @Test + public void testOnPreferenceTreeClick_noAudioAttributes() { + NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); + NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); + channel.setSound(null, null); + mController.onResume(appRow, channel, null, null, null, null, null); + + AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); + NotificationSoundPreference pref = + spy(new NotificationSoundPreference(mContext, attributeSet)); + pref.setKey(mController.getPreferenceKey()); + mController.handlePreferenceTreeClick(pref); + + ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(pref, times(1)).onPrepareRingtonePickerIntent(intentArgumentCaptor.capture()); + assertEquals(RingtoneManager.TYPE_NOTIFICATION, + intentArgumentCaptor.getValue().getIntExtra( + RingtoneManager.EXTRA_RINGTONE_TYPE, 0)); + } + @Test public void testOnActivityResult() { NotificationSoundPreference pref = mock(NotificationSoundPreference.class); From c2f72f3ceae9e4b1e33dcd6097f094c55af51109 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 8 Nov 2024 04:04:40 +0000 Subject: [PATCH 09/13] Migrate Battery percentage preference into catalyst. Refactor logic from BatteryPercentagePreferenceController to BatteryPercentageSwitchPreference Bug: 372774754 Test: atest and manual test Flag: com.android.settings.flags.catalyst_power_usage_summary_screen NO_IFTTT=already updated. Change-Id: I9a259b966a832293e067c9f06c4c98479cb538a2 --- ...BatteryPercentagePreferenceController.java | 4 +- .../BatteryPercentageSwitchPreference.kt | 95 +++++++++++++ .../batteryusage/PowerUsageSummaryScreen.kt | 12 +- ...eryPercentagePreferenceControllerTest.java | 4 +- .../BatteryPercentageSwitchPreferenceTest.kt | 130 ++++++++++++++++++ 5 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 src/com/android/settings/display/BatteryPercentageSwitchPreference.kt create mode 100644 tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt diff --git a/src/com/android/settings/display/BatteryPercentagePreferenceController.java b/src/com/android/settings/display/BatteryPercentagePreferenceController.java index a7113b3d490..b859c78af5f 100644 --- a/src/com/android/settings/display/BatteryPercentagePreferenceController.java +++ b/src/com/android/settings/display/BatteryPercentagePreferenceController.java @@ -35,6 +35,7 @@ import com.android.settings.overlay.FeatureFactory; * A controller to manage the switch for showing battery percentage in the status bar. */ +// LINT.IfChange public class BatteryPercentagePreferenceController extends BasePreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { @@ -69,7 +70,7 @@ public class BatteryPercentagePreferenceController extends BasePreferenceControl int setting = Settings.System.getInt(mContext.getContentResolver(), SHOW_BATTERY_PERCENT, mContext.getResources().getBoolean( - R.bool.config_defaultBatteryPercentageSetting) ? 1 : 0); + R.bool.config_defaultBatteryPercentageSetting) ? 1 : 0); ((TwoStatePreference) preference).setChecked(setting == 1); } @@ -84,3 +85,4 @@ public class BatteryPercentagePreferenceController extends BasePreferenceControl return true; } } +// LINT.ThenChange(BatteryPercentageSwitchPreference.kt) diff --git a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt new file mode 100644 index 00000000000..8571cf18c85 --- /dev/null +++ b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.display + +import android.app.settings.SettingsEnums +import android.content.Context +import android.provider.Settings +import androidx.preference.Preference +import com.android.settings.R +import com.android.settings.Utils +import com.android.settings.overlay.FeatureFactory.Companion.featureFactory +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.KeyedObservableDelegate +import com.android.settingslib.datastore.SettingsStore +import com.android.settingslib.datastore.SettingsSystemStore +import com.android.settingslib.metadata.PreferenceAvailabilityProvider +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.ReadWritePermit +import com.android.settingslib.metadata.SwitchPreference +import com.android.settingslib.preference.SwitchPreferenceBinding + +// LINT.IfChange +class BatteryPercentageSwitchPreference : + SwitchPreference(KEY, R.string.battery_percentage, R.string.battery_percentage_description), + SwitchPreferenceBinding, + PreferenceAvailabilityProvider, + Preference.OnPreferenceChangeListener { + + override fun storage(context: Context): KeyValueStore = + BatteryPercentageStorage(context, SettingsSystemStore.get(context)) + + override fun isAvailable(context: Context): Boolean = + Utils.isBatteryPresent(context) && + context.resources.getBoolean( + com.android.internal.R.bool.config_battery_percentage_setting_available + ) + + override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = + ReadWritePermit.ALLOW + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + preference.onPreferenceChangeListener = this + } + + override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { + val showPercentage = newValue as Boolean + + featureFactory.metricsFeatureProvider.action( + preference.context, + SettingsEnums.OPEN_BATTERY_PERCENTAGE, + showPercentage, + ) + return true + } + + @Suppress("UNCHECKED_CAST") + private class BatteryPercentageStorage( + private val context: Context, + private val settingsStore: SettingsStore, + ) : KeyedObservableDelegate(settingsStore), KeyValueStore { + + override fun contains(key: String) = settingsStore.contains(KEY) + + override fun getValue(key: String, valueType: Class) = + (settingsStore.getBoolean(key) ?: getDefaultValue(key, valueType)) as T + + override fun setValue(key: String, valueType: Class, value: T?) { + settingsStore.setBoolean(key, value as Boolean) + } + + override fun getDefaultValue(key: String, valueType: Class) = + context.resources.getBoolean( + com.android.internal.R.bool.config_defaultBatteryPercentageSetting + ) as T + } + + companion object { + const val KEY = Settings.System.SHOW_BATTERY_PERCENT + } +} +// LINT.ThenChange(BatteryPercentagePreferenceController.java) diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt index 229e3084081..b397bf3ded6 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt +++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt @@ -17,6 +17,7 @@ package com.android.settings.fuelgauge.batteryusage import android.content.Context import com.android.settings.R +import com.android.settings.display.BatteryPercentageSwitchPreference import com.android.settings.flags.Flags import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceIconProvider @@ -25,9 +26,8 @@ import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceScreenCreator @ProvidePreferenceScreen -class PowerUsageSummaryScreen : PreferenceScreenCreator, - PreferenceAvailabilityProvider, - PreferenceIconProvider { +class PowerUsageSummaryScreen : + PreferenceScreenCreator, PreferenceAvailabilityProvider, PreferenceIconProvider { override val key: String get() = KEY @@ -53,10 +53,10 @@ class PowerUsageSummaryScreen : PreferenceScreenCreator, R.drawable.ic_settings_battery_white } - - override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} + override fun getPreferenceHierarchy(context: Context) = + preferenceHierarchy(this) { +BatteryPercentageSwitchPreference() } companion object { const val KEY = "power_usage_summary_screen" } -} \ No newline at end of file +} diff --git a/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java index c84127b04a1..676ae2bfd90 100644 --- a/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java @@ -37,9 +37,10 @@ import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowUtils.class) +// LINT.IfChange public class BatteryPercentagePreferenceControllerTest { - private static final String PREF_KEY = "battery_percentage"; + private static final String PREF_KEY = "status_bar_show_battery_percent"; private Context mContext; private BatteryPercentagePreferenceController mController; @@ -80,3 +81,4 @@ public class BatteryPercentagePreferenceControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); } } +// LINT.ThenChange(BatteryPercentageSwitchPreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt b/tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt new file mode 100644 index 00000000000..d9a12d57564 --- /dev/null +++ b/tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.display + +import android.content.Context +import android.content.ContextWrapper +import android.content.res.Resources +import android.provider.Settings +import androidx.preference.SwitchPreferenceCompat +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.accessibility.AccessibilityUtil.State.OFF +import com.android.settings.accessibility.AccessibilityUtil.State.ON +import com.android.settings.testutils.shadow.ShadowUtils +import com.android.settingslib.preference.createAndBindWidget +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +@Config(shadows = [ShadowUtils::class]) +// LINT.IfChange +class BatteryPercentageSwitchPreferenceTest { + private val mockResources = mock() + + private val appContext: Context = ApplicationProvider.getApplicationContext() + + private val context = + object : ContextWrapper(appContext) { + override fun getResources(): Resources = mockResources + } + + private val batteryPercentageSwitchPreference = BatteryPercentageSwitchPreference() + + @After + fun tearDown() { + ShadowUtils.reset() + } + + @Test + fun isAvailable_noBatteryPresent_shouldReturnFalse() { + ShadowUtils.setIsBatteryPresent(false) + + assertThat(batteryPercentageSwitchPreference.isAvailable(context)).isFalse() + } + + @Test + fun isAvailable_batterySettingsAvailable_shouldReturnTrue() { + ShadowUtils.setIsBatteryPresent(true) + mockResources.stub { on { getBoolean(anyInt()) } doReturn true } + + assertThat(batteryPercentageSwitchPreference.isAvailable(context)).isTrue() + } + + @Test + fun isAvailable_batterySettingsUnavailable_shouldReturnFalse() { + ShadowUtils.setIsBatteryPresent(true) + mockResources.stub { on { getBoolean(anyInt()) } doReturn false } + + assertThat(batteryPercentageSwitchPreference.isAvailable(context)).isFalse() + } + + @Test + fun batteryPercentageEnabled_shouldSwitchPreferenceChecked() { + showBatteryPercentage(true) + + val switchPreference = getSwitchPreferenceCompat() + + assertThat(switchPreference.isChecked).isTrue() + } + + @Test + fun batteryPercentageDisabled_shouldSwitchPreferenceUnChecked() { + showBatteryPercentage(false) + + val switchPreference = getSwitchPreferenceCompat() + + assertThat(switchPreference.isChecked).isFalse() + } + + @Test + fun click_defaultBatteryPercentageDisabled_shouldChangeToEnabled() { + showBatteryPercentage(false) + + val switchPreference = getSwitchPreferenceCompat().apply { performClick() } + + assertThat(switchPreference.isChecked).isTrue() + } + + @Test + fun click_defaultBatteryPercentageEnabled_shouldChangeToDisabled() { + showBatteryPercentage(true) + + val switchPreference = getSwitchPreferenceCompat().apply { performClick() } + + assertThat(switchPreference.isChecked).isFalse() + } + + private fun getSwitchPreferenceCompat(): SwitchPreferenceCompat = + batteryPercentageSwitchPreference.createAndBindWidget(context) + + private fun showBatteryPercentage(on: Boolean) = + batteryPercentageSwitchPreference + .storage(context) + .setValue( + Settings.System.SHOW_BATTERY_PERCENT, + Boolean::class.javaObjectType, + on, + ) +} +// LINT.ThenChange(BatteryPercentagePreferenceControllerTest.java) From 88df8aa993033bc9aa5b18d1c9a543ed372a045e Mon Sep 17 00:00:00 2001 From: Yuchen Date: Thu, 7 Nov 2024 08:00:07 +0000 Subject: [PATCH 10/13] [Catalyst] Create datastore for AdaptiveConnectivityToggle. Bug: 368355361 Flag: com.android.settings.flags.catalyst_adaptive_connectivity Test: atest AdaptiveConnectivityTogglePreferenceTest AdaptiveConnectivityScreenTest NO_IFTTT=no added logic Change-Id: I8c1f78bb89c24903e2672d619605a0249924b05a --- .../AdaptiveConnectivityTogglePreference.kt | 46 ++++++---- ...daptiveConnectivityTogglePreferenceTest.kt | 88 ++++++++++++------- 2 files changed, 82 insertions(+), 52 deletions(-) diff --git a/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt b/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt index e1aea8519aa..4ce854672f9 100644 --- a/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt +++ b/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt @@ -19,35 +19,43 @@ package com.android.settings.network import android.content.Context import android.net.wifi.WifiManager import android.provider.Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED -import androidx.preference.Preference import com.android.settings.R +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.KeyedObservableDelegate import com.android.settingslib.datastore.SettingsSecureStore +import com.android.settingslib.datastore.SettingsStore import com.android.settingslib.metadata.MainSwitchPreference -import com.android.settingslib.metadata.PreferenceMetadata -import com.android.settingslib.preference.MainSwitchPreferenceBinding // LINT.IfChange class AdaptiveConnectivityTogglePreference : - MainSwitchPreference( - ADAPTIVE_CONNECTIVITY_ENABLED, - R.string.adaptive_connectivity_main_switch_title, - ), - MainSwitchPreferenceBinding, - Preference.OnPreferenceChangeListener { + MainSwitchPreference(KEY, R.string.adaptive_connectivity_main_switch_title) { - override fun storage(context: Context) = SettingsSecureStore.get(context) + override fun storage(context: Context): KeyValueStore = + AdaptiveConnectivityToggleStorage(context, SettingsSecureStore.get(context)) - override fun bind(preference: Preference, metadata: PreferenceMetadata) { - super.bind(preference, metadata) - preference.onPreferenceChangeListener = this + @Suppress("UNCHECKED_CAST") + private class AdaptiveConnectivityToggleStorage( + private val context: Context, + private val settingsStore: SettingsStore, + ) : KeyedObservableDelegate(settingsStore), KeyValueStore { + + override fun contains(key: String) = settingsStore.contains(KEY) + + override fun getDefaultValue(key: String, valueType: Class) = + DEFAULT_VALUE as T + + override fun getValue(key: String, valueType: Class) = + (settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T + + override fun setValue(key: String, valueType: Class, value: T?) { + settingsStore.setBoolean(key, value as Boolean) + context.getSystemService(WifiManager::class.java)?.setWifiScoringEnabled(value) + } } - override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { - val isChecked = newValue as Boolean - preference.context - .getSystemService(WifiManager::class.java) - ?.setWifiScoringEnabled(isChecked) - return true + companion object { + const val KEY = ADAPTIVE_CONNECTIVITY_ENABLED + const val DEFAULT_VALUE = true } } // LINT.ThenChange(AdaptiveConnectivityTogglePreferenceController.java) diff --git a/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceTest.kt b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceTest.kt index d85980efe67..c0ad07eec3b 100644 --- a/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceTest.kt +++ b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceTest.kt @@ -17,68 +17,90 @@ package com.android.settings.network import android.content.Context +import android.content.ContextWrapper import android.net.wifi.WifiManager -import android.platform.test.flag.junit.SetFlagsRule -import android.provider.Settings +import android.provider.Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.preference.createAndBindWidget import com.android.settingslib.widget.MainSwitchPreference import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.mock -import org.mockito.kotlin.spy import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever // LINT.IfChange @RunWith(AndroidJUnit4::class) class AdaptiveConnectivityTogglePreferenceTest { - @get:Rule - val setFlagsRule = SetFlagsRule() + private val mockWifiManager = mock() - private val appContext: Context = spy(ApplicationProvider.getApplicationContext()){} - - private val mockWifiManager: WifiManager = mock() + private val context: Context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getSystemService(name: String): Any? = + when { + name == getSystemServiceName(WifiManager::class.java) -> mockWifiManager + else -> super.getSystemService(name) + } + } private val adaptiveConnectivityTogglePreference = AdaptiveConnectivityTogglePreference() - @Before - fun setUp() { - whenever(appContext.getSystemService(WifiManager::class.java)).thenReturn(mockWifiManager) + @Test + fun switchClick_defaultDisabled_returnFalse() { + setAdaptiveConnectivityEnabled(false) + + assertThat(getMainSwitchPreference().isChecked).isFalse() } @Test - fun setChecked_withTrue_shouldUpdateSetting() { - Settings.Secure.putInt( - appContext.contentResolver, - Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 0 - ) + fun switchClick_defaultEnabled_returnTrue() { + setAdaptiveConnectivityEnabled(true) - val mainSwitchPreference = getMainSwitchPreferenceCompat().apply { performClick() } - - assertThat(mainSwitchPreference.isChecked).isTrue() - verify(mockWifiManager, atLeastOnce()).setWifiScoringEnabled(true) + assertThat(getMainSwitchPreference().isChecked).isTrue() } @Test - fun setChecked_withFalse_shouldUpdateSetting() { - Settings.Secure.putInt( - appContext.contentResolver, - Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1 - ) + fun setChecked_defaultEnabled_updatesCorrectly() { + val preference = getMainSwitchPreference() + assertThat(preference.isChecked).isTrue() - val mainSwitchPreference = getMainSwitchPreferenceCompat().apply { performClick() } + preference.performClick() - assertThat(mainSwitchPreference.isChecked).isFalse() + assertThat(preference.isChecked).isFalse() + + preference.performClick() + + assertThat(preference.isChecked).isTrue() + } + + @Test + fun storeSetTrue_wifiManagerSetWifiScoringEnabled() { + setAdaptiveConnectivityEnabled(true) + + assertThat(getAdaptiveConnectivityEnabled()).isTrue() + verify(mockWifiManager).setWifiScoringEnabled(true) + } + + @Test + fun storeSetFalse_wifiManagerSetWifiScoringDisabled() { + setAdaptiveConnectivityEnabled(false) + + assertThat(getAdaptiveConnectivityEnabled()).isFalse() verify(mockWifiManager).setWifiScoringEnabled(false) } - private fun getMainSwitchPreferenceCompat(): MainSwitchPreference = - adaptiveConnectivityTogglePreference.createAndBindWidget(appContext) + private fun getMainSwitchPreference(): MainSwitchPreference = + adaptiveConnectivityTogglePreference.createAndBindWidget(context) + + private fun setAdaptiveConnectivityEnabled(enabled: Boolean) = + adaptiveConnectivityTogglePreference + .storage(context) + .setValue(ADAPTIVE_CONNECTIVITY_ENABLED, Boolean::class.javaObjectType, enabled) + + private fun getAdaptiveConnectivityEnabled() = + adaptiveConnectivityTogglePreference + .storage(context) + .getValue(ADAPTIVE_CONNECTIVITY_ENABLED, Boolean::class.javaObjectType) } // LINT.ThenChange(AdaptiveConnectivityTogglePreferenceControllerTest.java) From 9a91334dc905150a163432d82d6dc21a63f363ba Mon Sep 17 00:00:00 2001 From: Jay Wang Date: Wed, 13 Nov 2024 03:15:56 +0000 Subject: [PATCH 11/13] RESTRICT AUTOMERGE Add battery replacement strings Bug: 376148038 Test: local test Change-Id: I9d734e4c08da50fd018c512f767ad0432a175b86 --- res/values/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index 1e48a974afa..0bf7dca2bbb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6478,6 +6478,10 @@ Tablet has typical background battery usage Device has typical background battery usage + + Battery replacement recommended + + Battery capacity and charging performance are reduced, and battery replacement is recommended. Battery level low @@ -8236,6 +8240,8 @@ + + From a9b4073e0aa6ee4d6ed53a2619ef049e59e8594f Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 13 Nov 2024 13:15:07 +0800 Subject: [PATCH 12/13] Clean up ApnSettings unused fields Bug: 374226933 Flag: EXEMPT clean up Test: manual - on ApnSettings Change-Id: If5e4aa6b8e10a138284f97eb840065895d4e5371 --- res/xml/apn_editor.xml | 177 ------------------ .../settings/network/apn/ApnSettings.java | 32 +--- 2 files changed, 4 insertions(+), 205 deletions(-) delete mode 100644 res/xml/apn_editor.xml diff --git a/res/xml/apn_editor.xml b/res/xml/apn_editor.xml deleted file mode 100644 index e5c04e02a24..00000000000 --- a/res/xml/apn_editor.xml +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/com/android/settings/network/apn/ApnSettings.java b/src/com/android/settings/network/apn/ApnSettings.java index 55de5db5029..24f25ecf7ac 100644 --- a/src/com/android/settings/network/apn/ApnSettings.java +++ b/src/com/android/settings/network/apn/ApnSettings.java @@ -23,7 +23,6 @@ import android.app.Dialog; import android.app.ProgressDialog; import android.app.settings.SettingsEnums; import android.content.Context; -import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -34,7 +33,6 @@ import android.provider.Telephony; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.data.ApnSetting; -import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -51,7 +49,6 @@ import androidx.preference.PreferenceGroup; import com.android.settings.R; import com.android.settings.RestrictedSettingsFragment; -import com.android.settings.flags.Flags; import com.android.settings.network.telephony.SubscriptionRepository; import com.android.settings.spa.SpaActivity; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -69,16 +66,12 @@ public class ApnSettings extends RestrictedSettingsFragment public static final String APN_ID = "apn_id"; public static final String APN_LIST = "apn_list"; public static final String SUB_ID = "sub_id"; - public static final String MVNO_TYPE = "mvno_type"; - public static final String MVNO_MATCH_DATA = "mvno_match_data"; private static final String[] CARRIERS_PROJECTION = new String[] { Telephony.Carriers._ID, Telephony.Carriers.NAME, Telephony.Carriers.APN, Telephony.Carriers.TYPE, - Telephony.Carriers.MVNO_TYPE, - Telephony.Carriers.MVNO_MATCH_DATA, Telephony.Carriers.EDITED_STATUS, }; @@ -86,9 +79,7 @@ public class ApnSettings extends RestrictedSettingsFragment private static final int NAME_INDEX = 1; private static final int APN_INDEX = 2; private static final int TYPES_INDEX = 3; - private static final int MVNO_TYPE_INDEX = 4; - private static final int MVNO_MATCH_DATA_INDEX = 5; - private static final int EDITED_INDEX = 6; + private static final int EDITED_INDEX = 4; private static final int MENU_NEW = Menu.FIRST; private static final int MENU_RESTORE = Menu.FIRST + 1; @@ -102,8 +93,6 @@ public class ApnSettings extends RestrictedSettingsFragment private PreferredApnRepository mPreferredApnRepository; @Nullable private String mPreferredApnKey; - private String mMvnoType; - private String mMvnoMatchData; private boolean mUnavailable; @@ -242,8 +231,6 @@ public class ApnSettings extends RestrictedSettingsFragment final String key = cursor.getString(ID_INDEX); final String type = cursor.getString(TYPES_INDEX); final int edited = cursor.getInt(EDITED_INDEX); - mMvnoType = cursor.getString(MVNO_TYPE_INDEX); - mMvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX); final ApnPreference pref = new ApnPreference(getPrefContext()); @@ -311,20 +298,9 @@ public class ApnSettings extends RestrictedSettingsFragment } private void addNewApn() { - if (Flags.newApnPageEnabled()) { - String route = ApnEditPageProvider.INSTANCE.getRoute( - INSERT_URL, Telephony.Carriers.CONTENT_URI, mSubId); - SpaActivity.startSpaActivity(getContext(), route); - } else { - final Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI); - intent.putExtra(SUB_ID, mSubId); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) { - intent.putExtra(MVNO_TYPE, mMvnoType); - intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData); - } - startActivity(intent); - } + String route = ApnEditPageProvider.INSTANCE.getRoute( + INSERT_URL, Telephony.Carriers.CONTENT_URI, mSubId); + SpaActivity.startSpaActivity(getContext(), route); } @Override From 1bbe798c8f7a36253660d37785d23f2d3cb5efe2 Mon Sep 17 00:00:00 2001 From: "ELIYAZ MOMIN (xWF)" Date: Wed, 13 Nov 2024 13:39:43 +0000 Subject: [PATCH 13/13] Revert "Migrate Battery percentage preference into catalyst. Ref..." Revert submission 30294757-catalyst_battery_percentage Reason for revert: Reverted changes: /q/submissionid:30294757-catalyst_battery_percentage Change-Id: I17a124619a1d6c7b6930a1c26c2b84c1a52ce8f7 --- ...BatteryPercentagePreferenceController.java | 4 +- .../BatteryPercentageSwitchPreference.kt | 95 ------------- .../batteryusage/PowerUsageSummaryScreen.kt | 12 +- ...eryPercentagePreferenceControllerTest.java | 4 +- .../BatteryPercentageSwitchPreferenceTest.kt | 130 ------------------ 5 files changed, 8 insertions(+), 237 deletions(-) delete mode 100644 src/com/android/settings/display/BatteryPercentageSwitchPreference.kt delete mode 100644 tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt diff --git a/src/com/android/settings/display/BatteryPercentagePreferenceController.java b/src/com/android/settings/display/BatteryPercentagePreferenceController.java index b859c78af5f..a7113b3d490 100644 --- a/src/com/android/settings/display/BatteryPercentagePreferenceController.java +++ b/src/com/android/settings/display/BatteryPercentagePreferenceController.java @@ -35,7 +35,6 @@ import com.android.settings.overlay.FeatureFactory; * A controller to manage the switch for showing battery percentage in the status bar. */ -// LINT.IfChange public class BatteryPercentagePreferenceController extends BasePreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { @@ -70,7 +69,7 @@ public class BatteryPercentagePreferenceController extends BasePreferenceControl int setting = Settings.System.getInt(mContext.getContentResolver(), SHOW_BATTERY_PERCENT, mContext.getResources().getBoolean( - R.bool.config_defaultBatteryPercentageSetting) ? 1 : 0); + R.bool.config_defaultBatteryPercentageSetting) ? 1 : 0); ((TwoStatePreference) preference).setChecked(setting == 1); } @@ -85,4 +84,3 @@ public class BatteryPercentagePreferenceController extends BasePreferenceControl return true; } } -// LINT.ThenChange(BatteryPercentageSwitchPreference.kt) diff --git a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt deleted file mode 100644 index 8571cf18c85..00000000000 --- a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settings.display - -import android.app.settings.SettingsEnums -import android.content.Context -import android.provider.Settings -import androidx.preference.Preference -import com.android.settings.R -import com.android.settings.Utils -import com.android.settings.overlay.FeatureFactory.Companion.featureFactory -import com.android.settingslib.datastore.KeyValueStore -import com.android.settingslib.datastore.KeyedObservableDelegate -import com.android.settingslib.datastore.SettingsStore -import com.android.settingslib.datastore.SettingsSystemStore -import com.android.settingslib.metadata.PreferenceAvailabilityProvider -import com.android.settingslib.metadata.PreferenceMetadata -import com.android.settingslib.metadata.ReadWritePermit -import com.android.settingslib.metadata.SwitchPreference -import com.android.settingslib.preference.SwitchPreferenceBinding - -// LINT.IfChange -class BatteryPercentageSwitchPreference : - SwitchPreference(KEY, R.string.battery_percentage, R.string.battery_percentage_description), - SwitchPreferenceBinding, - PreferenceAvailabilityProvider, - Preference.OnPreferenceChangeListener { - - override fun storage(context: Context): KeyValueStore = - BatteryPercentageStorage(context, SettingsSystemStore.get(context)) - - override fun isAvailable(context: Context): Boolean = - Utils.isBatteryPresent(context) && - context.resources.getBoolean( - com.android.internal.R.bool.config_battery_percentage_setting_available - ) - - override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = - ReadWritePermit.ALLOW - - override fun bind(preference: Preference, metadata: PreferenceMetadata) { - super.bind(preference, metadata) - preference.onPreferenceChangeListener = this - } - - override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { - val showPercentage = newValue as Boolean - - featureFactory.metricsFeatureProvider.action( - preference.context, - SettingsEnums.OPEN_BATTERY_PERCENTAGE, - showPercentage, - ) - return true - } - - @Suppress("UNCHECKED_CAST") - private class BatteryPercentageStorage( - private val context: Context, - private val settingsStore: SettingsStore, - ) : KeyedObservableDelegate(settingsStore), KeyValueStore { - - override fun contains(key: String) = settingsStore.contains(KEY) - - override fun getValue(key: String, valueType: Class) = - (settingsStore.getBoolean(key) ?: getDefaultValue(key, valueType)) as T - - override fun setValue(key: String, valueType: Class, value: T?) { - settingsStore.setBoolean(key, value as Boolean) - } - - override fun getDefaultValue(key: String, valueType: Class) = - context.resources.getBoolean( - com.android.internal.R.bool.config_defaultBatteryPercentageSetting - ) as T - } - - companion object { - const val KEY = Settings.System.SHOW_BATTERY_PERCENT - } -} -// LINT.ThenChange(BatteryPercentagePreferenceController.java) diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt index b397bf3ded6..229e3084081 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt +++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryScreen.kt @@ -17,7 +17,6 @@ package com.android.settings.fuelgauge.batteryusage import android.content.Context import com.android.settings.R -import com.android.settings.display.BatteryPercentageSwitchPreference import com.android.settings.flags.Flags import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceIconProvider @@ -26,8 +25,9 @@ import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceScreenCreator @ProvidePreferenceScreen -class PowerUsageSummaryScreen : - PreferenceScreenCreator, PreferenceAvailabilityProvider, PreferenceIconProvider { +class PowerUsageSummaryScreen : PreferenceScreenCreator, + PreferenceAvailabilityProvider, + PreferenceIconProvider { override val key: String get() = KEY @@ -53,10 +53,10 @@ class PowerUsageSummaryScreen : R.drawable.ic_settings_battery_white } - override fun getPreferenceHierarchy(context: Context) = - preferenceHierarchy(this) { +BatteryPercentageSwitchPreference() } + + override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} companion object { const val KEY = "power_usage_summary_screen" } -} +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java index 676ae2bfd90..c84127b04a1 100644 --- a/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java @@ -37,10 +37,9 @@ import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowUtils.class) -// LINT.IfChange public class BatteryPercentagePreferenceControllerTest { - private static final String PREF_KEY = "status_bar_show_battery_percent"; + private static final String PREF_KEY = "battery_percentage"; private Context mContext; private BatteryPercentagePreferenceController mController; @@ -81,4 +80,3 @@ public class BatteryPercentagePreferenceControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); } } -// LINT.ThenChange(BatteryPercentageSwitchPreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt b/tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt deleted file mode 100644 index d9a12d57564..00000000000 --- a/tests/robotests/src/com/android/settings/display/BatteryPercentageSwitchPreferenceTest.kt +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settings.display - -import android.content.Context -import android.content.ContextWrapper -import android.content.res.Resources -import android.provider.Settings -import androidx.preference.SwitchPreferenceCompat -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settings.accessibility.AccessibilityUtil.State.OFF -import com.android.settings.accessibility.AccessibilityUtil.State.ON -import com.android.settings.testutils.shadow.ShadowUtils -import com.android.settingslib.preference.createAndBindWidget -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.stub -import org.robolectric.annotation.Config - -@RunWith(AndroidJUnit4::class) -@Config(shadows = [ShadowUtils::class]) -// LINT.IfChange -class BatteryPercentageSwitchPreferenceTest { - private val mockResources = mock() - - private val appContext: Context = ApplicationProvider.getApplicationContext() - - private val context = - object : ContextWrapper(appContext) { - override fun getResources(): Resources = mockResources - } - - private val batteryPercentageSwitchPreference = BatteryPercentageSwitchPreference() - - @After - fun tearDown() { - ShadowUtils.reset() - } - - @Test - fun isAvailable_noBatteryPresent_shouldReturnFalse() { - ShadowUtils.setIsBatteryPresent(false) - - assertThat(batteryPercentageSwitchPreference.isAvailable(context)).isFalse() - } - - @Test - fun isAvailable_batterySettingsAvailable_shouldReturnTrue() { - ShadowUtils.setIsBatteryPresent(true) - mockResources.stub { on { getBoolean(anyInt()) } doReturn true } - - assertThat(batteryPercentageSwitchPreference.isAvailable(context)).isTrue() - } - - @Test - fun isAvailable_batterySettingsUnavailable_shouldReturnFalse() { - ShadowUtils.setIsBatteryPresent(true) - mockResources.stub { on { getBoolean(anyInt()) } doReturn false } - - assertThat(batteryPercentageSwitchPreference.isAvailable(context)).isFalse() - } - - @Test - fun batteryPercentageEnabled_shouldSwitchPreferenceChecked() { - showBatteryPercentage(true) - - val switchPreference = getSwitchPreferenceCompat() - - assertThat(switchPreference.isChecked).isTrue() - } - - @Test - fun batteryPercentageDisabled_shouldSwitchPreferenceUnChecked() { - showBatteryPercentage(false) - - val switchPreference = getSwitchPreferenceCompat() - - assertThat(switchPreference.isChecked).isFalse() - } - - @Test - fun click_defaultBatteryPercentageDisabled_shouldChangeToEnabled() { - showBatteryPercentage(false) - - val switchPreference = getSwitchPreferenceCompat().apply { performClick() } - - assertThat(switchPreference.isChecked).isTrue() - } - - @Test - fun click_defaultBatteryPercentageEnabled_shouldChangeToDisabled() { - showBatteryPercentage(true) - - val switchPreference = getSwitchPreferenceCompat().apply { performClick() } - - assertThat(switchPreference.isChecked).isFalse() - } - - private fun getSwitchPreferenceCompat(): SwitchPreferenceCompat = - batteryPercentageSwitchPreference.createAndBindWidget(context) - - private fun showBatteryPercentage(on: Boolean) = - batteryPercentageSwitchPreference - .storage(context) - .setValue( - Settings.System.SHOW_BATTERY_PERCENT, - Boolean::class.javaObjectType, - on, - ) -} -// LINT.ThenChange(BatteryPercentagePreferenceControllerTest.java)