summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yuri Lin <yurilin@google.com> 2025-03-24 12:13:24 -0700
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-24 12:13:24 -0700
commit0773c4d93fb56aa851825cb12d516b4ac36b30a3 (patch)
treef4dad7eb5b2ef53cfcabdd82800fd9fb8c05b5a4
parent06be21bc9c4540cb03e6deb04ce19226860070dc (diff)
parent53f94f049574621ba10121165ef099b1c1c50279 (diff)
Merge "Merge bundle global & type preference controllers." into main
-rw-r--r--res/xml/bundle_notifications_settings.xml39
-rw-r--r--src/com/android/settings/notification/BundleCombinedPreferenceController.java151
-rw-r--r--src/com/android/settings/notification/BundleGlobalPreferenceController.java60
-rw-r--r--src/com/android/settings/notification/BundlePreferenceFragment.java20
-rw-r--r--src/com/android/settings/notification/BundleTypePreferenceController.java82
-rw-r--r--src/com/android/settings/notification/NotificationBackend.java13
-rw-r--r--tests/robotests/src/com/android/settings/notification/BundleCombinedPreferenceControllerTest.java229
-rw-r--r--tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java101
-rw-r--r--tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java171
9 files changed, 427 insertions, 439 deletions
diff --git a/res/xml/bundle_notifications_settings.xml b/res/xml/bundle_notifications_settings.xml
index 3ff8c053273..0f153de81a6 100644
--- a/res/xml/bundle_notifications_settings.xml
+++ b/res/xml/bundle_notifications_settings.xml
@@ -32,30 +32,29 @@
settings:searchable="false"
android:title="@string/notification_bundle_description"/>
- <com.android.settingslib.widget.MainSwitchPreference
- android:key="global_pref"
- android:title="@string/notification_bundle_main_control_title"
- settings:controller="com.android.settings.notification.BundleGlobalPreferenceController" />
+ <PreferenceCategory
+ android:key="enabled_settings">
+
+ <com.android.settingslib.widget.MainSwitchPreference
+ android:key="global_pref"
+ android:title="@string/notification_bundle_main_control_title" />
- <CheckBoxPreference
- android:key="promotions"
- android:title="@*android:string/promotional_notification_channel_label"
- settings:controller="com.android.settings.notification.BundleTypePreferenceController"/>
+ <CheckBoxPreference
+ android:key="promotions"
+ android:title="@*android:string/promotional_notification_channel_label"/>
- <CheckBoxPreference
- android:key="news"
- android:title="@*android:string/news_notification_channel_label"
- settings:controller="com.android.settings.notification.BundleTypePreferenceController"/>
+ <CheckBoxPreference
+ android:key="news"
+ android:title="@*android:string/news_notification_channel_label"/>
- <CheckBoxPreference
- android:key="social"
- android:title="@*android:string/social_notification_channel_label"
- settings:controller="com.android.settings.notification.BundleTypePreferenceController"/>
+ <CheckBoxPreference
+ android:key="social"
+ android:title="@*android:string/social_notification_channel_label"/>
- <CheckBoxPreference
- android:key="recs"
- android:title="@*android:string/recs_notification_channel_label"
- settings:controller="com.android.settings.notification.BundleTypePreferenceController"/>
+ <CheckBoxPreference
+ android:key="recs"
+ android:title="@*android:string/recs_notification_channel_label" />
+ </PreferenceCategory>
<PreferenceCategory
android:key="notification_bundle_excluded_apps_list"
diff --git a/src/com/android/settings/notification/BundleCombinedPreferenceController.java b/src/com/android/settings/notification/BundleCombinedPreferenceController.java
new file mode 100644
index 00000000000..cf9d7b8be4a
--- /dev/null
+++ b/src/com/android/settings/notification/BundleCombinedPreferenceController.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.Flags;
+import android.content.Context;
+import android.service.notification.Adjustment;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Preference controller governing both the global and individual type-based bundle preferences.
+ */
+public class BundleCombinedPreferenceController extends BasePreferenceController {
+
+ static final String GLOBAL_KEY = "global_pref";
+ static final String PROMO_KEY = "promotions";
+ static final String NEWS_KEY = "news";
+ static final String SOCIAL_KEY = "social";
+ static final String RECS_KEY = "recs";
+
+ static final List<String> ALL_PREF_TYPES = List.of(PROMO_KEY, NEWS_KEY, SOCIAL_KEY, RECS_KEY);
+
+ @NonNull NotificationBackend mBackend;
+
+ private @Nullable TwoStatePreference mGlobalPref;
+ private Map<String, TwoStatePreference> mTypePrefs = new ArrayMap<>();
+
+ public BundleCombinedPreferenceController(@NonNull Context context, @NonNull String prefKey,
+ @NonNull NotificationBackend backend) {
+ super(context, prefKey);
+ mBackend = backend;
+ }
+
+ @Override
+ @AvailabilityStatus
+ public int getAvailabilityStatus() {
+ if (Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ PreferenceCategory category = (PreferenceCategory) preference;
+
+ // Find and cache relevant preferences for later updates, then set values
+ mGlobalPref = category.findPreference(GLOBAL_KEY);
+ if (mGlobalPref != null) {
+ mGlobalPref.setOnPreferenceChangeListener(mGlobalPrefListener);
+ }
+ for (String key : ALL_PREF_TYPES) {
+ TwoStatePreference typePref = category.findPreference(key);
+ if (typePref != null) {
+ mTypePrefs.put(key, typePref);
+ typePref.setOnPreferenceChangeListener(getListenerForType(key));
+ }
+ }
+
+ updatePrefValues();
+ }
+
+ void updatePrefValues() {
+ boolean isBundlingEnabled = mBackend.isNotificationBundlingEnabled(mContext);
+ Set<Integer> allowedTypes = mBackend.getAllowedBundleTypes();
+
+ // State check: if bundling is globally enabled, but there are no allowed bundle types,
+ // disable the global bundling state from here before proceeding.
+ if (isBundlingEnabled && allowedTypes.size() == 0) {
+ mBackend.setNotificationBundlingEnabled(false);
+ isBundlingEnabled = false;
+ }
+
+ if (mGlobalPref != null) {
+ mGlobalPref.setChecked(isBundlingEnabled);
+ }
+
+ for (String key : mTypePrefs.keySet()) {
+ TwoStatePreference typePref = mTypePrefs.get(key);
+ // checkboxes for individual types should only be active if the global switch is on
+ typePref.setVisible(isBundlingEnabled);
+ if (isBundlingEnabled) {
+ typePref.setChecked(allowedTypes.contains(getBundleTypeForKey(key)));
+ }
+ }
+ }
+
+ private Preference.OnPreferenceChangeListener mGlobalPrefListener = (p, val) -> {
+ boolean checked = (boolean) val;
+ mBackend.setNotificationBundlingEnabled(checked);
+ // update state to hide or show preferences for individual types
+ updatePrefValues();
+ return true;
+ };
+
+ // Returns a preference listener for the given pref key that:
+ // * sets the backend state for whether that type is enabled
+ // * if it is disabled, trigger a new update sync global switch if needed
+ private Preference.OnPreferenceChangeListener getListenerForType(String prefKey) {
+ return (p, val) -> {
+ boolean checked = (boolean) val;
+ mBackend.setBundleTypeState(getBundleTypeForKey(prefKey), checked);
+ if (!checked) {
+ // goes from checked to un-checked; update state in case this was the last enabled
+ // individual category
+ updatePrefValues();
+ }
+ return true;
+ };
+ }
+
+ static @Adjustment.Types int getBundleTypeForKey(String preferenceKey) {
+ if (PROMO_KEY.equals(preferenceKey)) {
+ return Adjustment.TYPE_PROMOTION;
+ } else if (NEWS_KEY.equals(preferenceKey)) {
+ return Adjustment.TYPE_NEWS;
+ } else if (SOCIAL_KEY.equals(preferenceKey)) {
+ return Adjustment.TYPE_SOCIAL_MEDIA;
+ } else if (RECS_KEY.equals(preferenceKey)) {
+ return Adjustment.TYPE_CONTENT_RECOMMENDATION;
+ }
+ return Adjustment.TYPE_OTHER;
+ }
+
+}
diff --git a/src/com/android/settings/notification/BundleGlobalPreferenceController.java b/src/com/android/settings/notification/BundleGlobalPreferenceController.java
deleted file mode 100644
index 4df36ca716c..00000000000
--- a/src/com/android/settings/notification/BundleGlobalPreferenceController.java
+++ /dev/null
@@ -1,60 +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.notification;
-
-import android.app.Flags;
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import com.android.settings.core.TogglePreferenceController;
-
-public class BundleGlobalPreferenceController extends TogglePreferenceController {
-
- NotificationBackend mBackend;
-
- public BundleGlobalPreferenceController(@NonNull Context context,
- @NonNull String preferenceKey) {
- super(context, preferenceKey);
- mBackend = new NotificationBackend();
- }
-
- @Override
- public int getAvailabilityStatus() {
- if (Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()) {
- return AVAILABLE;
- }
- return CONDITIONALLY_UNAVAILABLE;
- }
-
- @Override
- public boolean isChecked() {
- return mBackend.isNotificationBundlingEnabled(mContext);
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- mBackend.setNotificationBundlingEnabled(isChecked);
- return true;
- }
-
- @Override
- public int getSliceHighlightMenuRes() {
- // not needed since it's not sliceable
- return NO_RES;
- }
-}
diff --git a/src/com/android/settings/notification/BundlePreferenceFragment.java b/src/com/android/settings/notification/BundlePreferenceFragment.java
index 4a61f9e54af..fe854a7f6bc 100644
--- a/src/com/android/settings/notification/BundlePreferenceFragment.java
+++ b/src/com/android/settings/notification/BundlePreferenceFragment.java
@@ -16,24 +16,23 @@
package com.android.settings.notification;
-import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.Adjustment.KEY_TYPE;
import android.app.Activity;
import android.app.Application;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.app.Flags;
-
-import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
-import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.List;
/**
* Fragment for bundled notifications.
@@ -41,6 +40,8 @@ import org.jetbrains.annotations.NotNull;
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class BundlePreferenceFragment extends DashboardFragment {
+ private static final String BUNDLE_CATEGORY_KEY = "enabled_settings";
+
@Override
public int getMetricsCategory() {
return SettingsEnums.BUNDLED_NOTIFICATIONS;
@@ -50,6 +51,15 @@ public class BundlePreferenceFragment extends DashboardFragment {
protected int getPreferenceScreenResId() {
return R.xml.bundle_notifications_settings;
}
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new BundleCombinedPreferenceController(context, BUNDLE_CATEGORY_KEY,
+ new NotificationBackend()));
+ return controllers;
+ }
+
@Override
protected String getLogTag() {
return "BundlePreferenceFragment";
diff --git a/src/com/android/settings/notification/BundleTypePreferenceController.java b/src/com/android/settings/notification/BundleTypePreferenceController.java
deleted file mode 100644
index d0786931ed7..00000000000
--- a/src/com/android/settings/notification/BundleTypePreferenceController.java
+++ /dev/null
@@ -1,82 +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.notification;
-
-import android.app.Flags;
-import android.content.Context;
-import android.service.notification.Adjustment;
-
-import androidx.annotation.NonNull;
-
-import com.android.settings.core.TogglePreferenceController;
-
-public class BundleTypePreferenceController extends TogglePreferenceController {
-
- static final String PROMO_KEY = "promotions";
- static final String NEWS_KEY = "news";
- static final String SOCIAL_KEY = "social";
- static final String RECS_KEY = "recs";
-
- NotificationBackend mBackend;
- int mType;
-
- public BundleTypePreferenceController(@NonNull Context context,
- @NonNull String preferenceKey) {
- super(context, preferenceKey);
- mBackend = new NotificationBackend();
- mType = getBundleTypeForKey();
- }
-
- @Override
- public int getAvailabilityStatus() {
- if (Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()
- && mBackend.isNotificationBundlingEnabled(mContext)) {
- return AVAILABLE;
- }
- return CONDITIONALLY_UNAVAILABLE;
- }
-
- @Override
- public boolean isChecked() {
- return mBackend.isBundleTypeApproved(mType);
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- mBackend.setBundleTypeState(mType, isChecked);
- return true;
- }
-
- @Override
- public int getSliceHighlightMenuRes() {
- // not needed since it's not sliceable
- return NO_RES;
- }
-
- private @Adjustment.Types int getBundleTypeForKey() {
- if (PROMO_KEY.equals(mPreferenceKey)) {
- return Adjustment.TYPE_PROMOTION;
- } else if (NEWS_KEY.equals(mPreferenceKey)) {
- return Adjustment.TYPE_NEWS;
- } else if (SOCIAL_KEY.equals(mPreferenceKey)) {
- return Adjustment.TYPE_SOCIAL_MEDIA;
- } else if (RECS_KEY.equals(mPreferenceKey)) {
- return Adjustment.TYPE_CONTENT_RECOMMENDATION;
- }
- return Adjustment.TYPE_OTHER;
- }
-}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 31972edcc1f..45bf9e3e23a 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -751,6 +751,19 @@ public class NotificationBackend {
return false;
}
+ public Set<Integer> getAllowedBundleTypes() {
+ try {
+ Set<Integer> allowed = new HashSet<>();
+ for (int type : sINM.getAllowedAdjustmentKeyTypes()) {
+ allowed.add(type);
+ }
+ return allowed;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return new HashSet<>();
+ }
+ }
+
public void setBundleTypeState(@Adjustment.Types int type, boolean enabled) {
try {
sINM.setAssistantAdjustmentKeyTypeState(type, enabled);
diff --git a/tests/robotests/src/com/android/settings/notification/BundleCombinedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BundleCombinedPreferenceControllerTest.java
new file mode 100644
index 00000000000..6d1c2c99a36
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BundleCombinedPreferenceControllerTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Flags;
+import android.content.Context;
+import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.Adjustment;
+
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.PreferenceCategory;
+
+import com.android.settingslib.widget.MainSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Set;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+public class BundleCombinedPreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private static final String PREFERENCE_KEY = "preference_key";
+
+ private Context mContext;
+ private BundleCombinedPreferenceController mController;
+
+ @Mock
+ private NotificationBackend mBackend;
+
+ @Mock
+ private PreferenceCategory mPrefCategory;
+
+ private MainSwitchPreference mGlobalSwitch;
+ private CheckBoxPreference mPromoCheckbox, mNewsCheckbox, mSocialCheckbox, mRecsCheckbox;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.getApplication();
+ mController = new BundleCombinedPreferenceController(mContext, PREFERENCE_KEY, mBackend);
+
+ // preference category/controller initiation
+ mGlobalSwitch = new MainSwitchPreference(mContext);
+ when(mPrefCategory.findPreference(
+ BundleCombinedPreferenceController.GLOBAL_KEY)).thenReturn(mGlobalSwitch);
+ mPromoCheckbox = new CheckBoxPreference(mContext);
+ when(mPrefCategory.findPreference(BundleCombinedPreferenceController.PROMO_KEY)).thenReturn(
+ mPromoCheckbox);
+ mNewsCheckbox = new CheckBoxPreference(mContext);
+ when(mPrefCategory.findPreference(BundleCombinedPreferenceController.NEWS_KEY)).thenReturn(
+ mNewsCheckbox);
+ mSocialCheckbox = new CheckBoxPreference(mContext);
+ when(mPrefCategory.findPreference(
+ BundleCombinedPreferenceController.SOCIAL_KEY)).thenReturn(mSocialCheckbox);
+ mRecsCheckbox = new CheckBoxPreference(mContext);
+ when(mPrefCategory.findPreference(BundleCombinedPreferenceController.RECS_KEY)).thenReturn(
+ mRecsCheckbox);
+
+ mController.updateState(mPrefCategory);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() {
+ when(mBackend.isNotificationBundlingSupported()).thenReturn(true);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse()
+ throws RemoteException {
+ when(mBackend.isNotificationBundlingSupported()).thenReturn(false);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() throws RemoteException {
+ when(mBackend.isNotificationBundlingSupported()).thenReturn(true);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void updatePrefValues_reflectsSettings() {
+ // bundling is enabled globally
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(true);
+
+ // allowed key types are promos & news
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_PROMOTION,
+ Adjustment.TYPE_NEWS));
+
+ mController.updatePrefValues();
+ assertThat(mGlobalSwitch.isChecked()).isTrue();
+ assertThat(mPromoCheckbox.isChecked()).isTrue();
+ assertThat(mNewsCheckbox.isChecked()).isTrue();
+ assertThat(mRecsCheckbox.isChecked()).isFalse();
+ assertThat(mSocialCheckbox.isChecked()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void updatePrefValues_typesGoneWhenGlobalOff() {
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(false);
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_PROMOTION,
+ Adjustment.TYPE_NEWS));
+
+ mController.updatePrefValues();
+ assertThat(mGlobalSwitch.isChecked()).isFalse();
+ assertThat(mPromoCheckbox.isVisible()).isFalse();
+ assertThat(mNewsCheckbox.isVisible()).isFalse();
+ assertThat(mRecsCheckbox.isVisible()).isFalse();
+ assertThat(mSocialCheckbox.isVisible()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void turnOffGlobalSwitch_updatesBackendAndTypeSwitches() {
+ // Initial state: global allowed + some types set
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(true);
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_PROMOTION,
+ Adjustment.TYPE_NEWS));
+ mController.updatePrefValues();
+
+ // Simulate the global switch turning off. This also requires telling the mock backend to
+ // start returning false before the click listener updates pref values
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(false);
+ mGlobalSwitch.getOnPreferenceChangeListener().onPreferenceChange(mGlobalSwitch, false);
+ verify(mBackend, times(1)).setNotificationBundlingEnabled(false);
+
+ // All individual type checkboxes should now not be visible.
+ assertThat(mPromoCheckbox.isVisible()).isFalse();
+ assertThat(mNewsCheckbox.isVisible()).isFalse();
+ assertThat(mRecsCheckbox.isVisible()).isFalse();
+ assertThat(mSocialCheckbox.isVisible()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void turnOnGlobalSwitch_updatesBackendAndTypeSwitches() {
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(false);
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_PROMOTION,
+ Adjustment.TYPE_NEWS));
+ mController.updatePrefValues();
+
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(true);
+ mGlobalSwitch.getOnPreferenceChangeListener().onPreferenceChange(mGlobalSwitch, true);
+ verify(mBackend, times(1)).setNotificationBundlingEnabled(true);
+
+ // type checkboxes should now exist & be checked accordingly to their state
+ assertThat(mPromoCheckbox.isChecked()).isTrue();
+ assertThat(mNewsCheckbox.isChecked()).isTrue();
+ assertThat(mRecsCheckbox.isChecked()).isFalse();
+ assertThat(mSocialCheckbox.isChecked()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void turnOnTypeBundle_updatesBackend_doesNotChangeGlobalSwitch() {
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(true);
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_SOCIAL_MEDIA));
+ mController.updatePrefValues();
+
+ mRecsCheckbox.getOnPreferenceChangeListener().onPreferenceChange(mRecsCheckbox, true);
+
+ // recs bundle setting should be updated in the backend, and global switch unchanged
+ verify(mBackend).setBundleTypeState(Adjustment.TYPE_CONTENT_RECOMMENDATION, true);
+ verify(mBackend, never()).setNotificationBundlingEnabled(anyBoolean());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ public void turnOffTypeBundle_lastOneChangesGlobalSwitch() {
+ when(mBackend.isNotificationBundlingEnabled(any())).thenReturn(true);
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_SOCIAL_MEDIA,
+ Adjustment.TYPE_CONTENT_RECOMMENDATION));
+ mController.updatePrefValues();
+
+ // Turning off one should update state, but not turn off the global setting
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of(Adjustment.TYPE_SOCIAL_MEDIA));
+ mRecsCheckbox.getOnPreferenceChangeListener().onPreferenceChange(mRecsCheckbox, false);
+ verify(mBackend).setBundleTypeState(Adjustment.TYPE_CONTENT_RECOMMENDATION, false);
+
+ // Now turn off the second
+ when(mBackend.getAllowedBundleTypes()).thenReturn(Set.of());
+ mSocialCheckbox.getOnPreferenceChangeListener().onPreferenceChange(mSocialCheckbox, false);
+ verify(mBackend).setBundleTypeState(Adjustment.TYPE_SOCIAL_MEDIA, false);
+
+ // This update should trigger a call to turn off the global switch
+ verify(mBackend).setNotificationBundlingEnabled(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java
deleted file mode 100644
index c389247389f..00000000000
--- a/tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java
+++ /dev/null
@@ -1,101 +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.notification;
-
-import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.Adjustment.KEY_TYPE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Flags;
-import android.app.INotificationManager;
-import android.content.Context;
-import android.platform.test.flag.junit.SetFlagsRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class BundleGlobalPreferenceControllerTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
- private static final String PREFERENCE_KEY = "preference_key";
-
- private Context mContext;
- BundleGlobalPreferenceController mController;
- @Mock
- INotificationManager mInm;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mSetFlagsRule.enableFlags(
- android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
- Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
- mController = new BundleGlobalPreferenceController(mContext, PREFERENCE_KEY);
- mController.mBackend.setNm(mInm);
- }
-
- @Test
- public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() {
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception {
- when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_TYPE));
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() {
- mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isChecked() throws Exception {
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
- assertThat(mController.isChecked()).isTrue();
-
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_IMPORTANCE));
- assertThat(mController.isChecked()).isFalse();
- }
-
- @Test
- public void setChecked() throws Exception {
- mController.setChecked(false);
- verify(mInm).disallowAssistantAdjustment(KEY_TYPE);
-
- mController.setChecked(true);
- verify(mInm).allowAssistantAdjustment(KEY_TYPE);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java
deleted file mode 100644
index 68bb1bb276e..00000000000
--- a/tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java
+++ /dev/null
@@ -1,171 +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.notification;
-
-import static android.service.notification.Adjustment.KEY_TYPE;
-import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
-import static android.service.notification.Adjustment.TYPE_NEWS;
-import static android.service.notification.Adjustment.TYPE_PROMOTION;
-import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Flags;
-import android.app.INotificationManager;
-import android.content.Context;
-import android.os.RemoteException;
-import android.platform.test.flag.junit.SetFlagsRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class BundleTypePreferenceControllerTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
- private static final String PREFERENCE_KEY = "preference_key";
-
- private Context mContext;
- BundleTypePreferenceController mController;
- @Mock
- INotificationManager mInm;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
- when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of());
- mSetFlagsRule.enableFlags(
- android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
- Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
- mContext = RuntimeEnvironment.application;
- mController = new BundleTypePreferenceController(mContext, PREFERENCE_KEY);
- mController.mBackend.setNm(mInm);
- }
-
- @Test
- public void isAvailable() throws RemoteException {
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse()
- throws RemoteException {
- when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_TYPE));
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() throws RemoteException {
- mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
- when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of());
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isAvailable_flagEnabledNasDisabled_shouldReturnFalse() throws RemoteException {
- when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of());
- when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of());
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isChecked_promotions() throws RemoteException {
- mController = new BundleTypePreferenceController(mContext,
- BundleTypePreferenceController.PROMO_KEY);
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{TYPE_PROMOTION});
- assertThat(mController.isChecked()).isTrue();
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
- assertThat(mController.isChecked()).isFalse();
- }
-
- @Test
- public void isChecked_news() throws RemoteException {
- mController = new BundleTypePreferenceController(mContext,
- BundleTypePreferenceController.NEWS_KEY);
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{TYPE_NEWS});
- assertThat(mController.isChecked()).isTrue();
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
- assertThat(mController.isChecked()).isFalse();
- }
-
- @Test
- public void isChecked_social() throws RemoteException {
- mController = new BundleTypePreferenceController(mContext,
- BundleTypePreferenceController.SOCIAL_KEY);
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{TYPE_SOCIAL_MEDIA});
- assertThat(mController.isChecked()).isTrue();
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
- assertThat(mController.isChecked()).isFalse();
- }
-
- @Test
- public void isChecked_recs() throws RemoteException {
- mController = new BundleTypePreferenceController(mContext,
- BundleTypePreferenceController.RECS_KEY);
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(
- new int[]{TYPE_CONTENT_RECOMMENDATION});
- assertThat(mController.isChecked()).isTrue();
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
- assertThat(mController.isChecked()).isFalse();
- }
-
- @Test
- public void isChecked_mixed() throws RemoteException {
- mController = new BundleTypePreferenceController(mContext,
- BundleTypePreferenceController.RECS_KEY);
-
- when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(
- new int[]{TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION});
- assertThat(mController.isChecked()).isTrue();
- }
-
- @Test
- public void setChecked() throws RemoteException {
- mController = new BundleTypePreferenceController(mContext,
- BundleTypePreferenceController.PROMO_KEY);
- mController.setChecked(false);
- verify(mInm).setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
-
- mController.setChecked(true);
- verify(mInm).setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, true);
- }
-}