Merge "Merge bundle global & type preference controllers." into main
This commit is contained in:
@@ -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">
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="promotions"
|
||||
android:title="@*android:string/promotional_notification_channel_label"
|
||||
settings:controller="com.android.settings.notification.BundleTypePreferenceController"/>
|
||||
<com.android.settingslib.widget.MainSwitchPreference
|
||||
android:key="global_pref"
|
||||
android:title="@string/notification_bundle_main_control_title" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="news"
|
||||
android:title="@*android:string/news_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="social"
|
||||
android:title="@*android:string/social_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="recs"
|
||||
android:title="@*android:string/recs_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" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="notification_bundle_excluded_apps_list"
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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";
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user