Merge "Move ZenModesBackend to SettingsLib." into main

This commit is contained in:
Ioana Alexandru
2024-06-21 16:50:27 +00:00
committed by Android (Google) Code Review
64 changed files with 165 additions and 1261 deletions

View File

@@ -1,28 +0,0 @@
<!--
Copyright (C) 2018 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,16.41 16.41,20 12,20z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M7,11h10v2h-10z"/>
</vector>

View File

@@ -105,7 +105,8 @@ public class DndConditionCardController implements ConditionalCardController {
+ mAppContext.getText(R.string.condition_zen_title))
.setTitleText(mAppContext.getText(R.string.condition_zen_title).toString())
.setSummaryText(getSummary())
.setIconDrawable(mAppContext.getDrawable(R.drawable.ic_do_not_disturb_on_24dp))
.setIconDrawable(mAppContext.getDrawable(
com.android.settingslib.R.drawable.ic_do_not_disturb_on_24dp))
.setViewType(ConditionContextualCardRenderer.VIEW_TYPE_HALF_WIDTH)
.build();
}

View File

@@ -26,6 +26,8 @@ import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.google.common.base.Preconditions;

View File

@@ -1,161 +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.modes;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
import android.app.AutomaticZenRule;
import android.content.Context;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.service.notification.SystemZenRules;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.content.res.AppCompatResources;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class IconLoader {
private static final String TAG = "ZenIconLoader";
private static final Drawable MISSING = new ColorDrawable();
@Nullable // Until first usage
private static IconLoader sInstance;
private final LruCache<String, Drawable> mCache;
private final ListeningExecutorService mBackgroundExecutor;
static IconLoader getInstance() {
if (sInstance == null) {
sInstance = new IconLoader();
}
return sInstance;
}
private IconLoader() {
this(Executors.newFixedThreadPool(4));
}
@VisibleForTesting
IconLoader(ExecutorService backgroundExecutor) {
mCache = new LruCache<>(50);
mBackgroundExecutor =
MoreExecutors.listeningDecorator(backgroundExecutor);
}
@NonNull
ListenableFuture<Drawable> getIcon(Context context, @NonNull AutomaticZenRule rule) {
if (rule.getIconResId() == 0) {
return Futures.immediateFuture(getFallbackIcon(context, rule.getType()));
}
return FluentFuture.from(loadIcon(context, rule.getPackageName(), rule.getIconResId()))
.transform(icon ->
icon != null ? icon : getFallbackIcon(context, rule.getType()),
MoreExecutors.directExecutor());
}
@NonNull
private ListenableFuture</* @Nullable */ Drawable> loadIcon(Context context, String pkg,
int iconResId) {
String cacheKey = pkg + ":" + iconResId;
synchronized (mCache) {
Drawable cachedValue = mCache.get(cacheKey);
if (cachedValue != null) {
return immediateFuture(cachedValue != MISSING ? cachedValue : null);
}
}
return FluentFuture.from(mBackgroundExecutor.submit(() -> {
if (TextUtils.isEmpty(pkg) || SystemZenRules.PACKAGE_ANDROID.equals(pkg)) {
return context.getDrawable(iconResId);
} else {
Context appContext = context.createPackageContext(pkg, 0);
Drawable appDrawable = AppCompatResources.getDrawable(appContext, iconResId);
return getMonochromeIconIfPresent(appDrawable);
}
})).catching(Exception.class, ex -> {
// If we cannot resolve the icon, then store MISSING in the cache below, so
// we don't try again.
Log.e(TAG, "Error while loading icon " + cacheKey, ex);
return null;
}, MoreExecutors.directExecutor()).transform(drawable -> {
synchronized (mCache) {
mCache.put(cacheKey, drawable != null ? drawable : MISSING);
}
return drawable;
}, MoreExecutors.directExecutor());
}
private static Drawable getFallbackIcon(Context context, int ruleType) {
int iconResIdFromType = switch (ruleType) {
case AutomaticZenRule.TYPE_UNKNOWN ->
com.android.internal.R.drawable.ic_zen_mode_type_unknown;
case AutomaticZenRule.TYPE_OTHER ->
com.android.internal.R.drawable.ic_zen_mode_type_other;
case AutomaticZenRule.TYPE_SCHEDULE_TIME ->
com.android.internal.R.drawable.ic_zen_mode_type_schedule_time;
case AutomaticZenRule.TYPE_SCHEDULE_CALENDAR ->
com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar;
case AutomaticZenRule.TYPE_BEDTIME ->
com.android.internal.R.drawable.ic_zen_mode_type_bedtime;
case AutomaticZenRule.TYPE_DRIVING ->
com.android.internal.R.drawable.ic_zen_mode_type_driving;
case AutomaticZenRule.TYPE_IMMERSIVE ->
com.android.internal.R.drawable.ic_zen_mode_type_immersive;
case AutomaticZenRule.TYPE_THEATER ->
com.android.internal.R.drawable.ic_zen_mode_type_theater;
case AutomaticZenRule.TYPE_MANAGED ->
com.android.internal.R.drawable.ic_zen_mode_type_managed;
default ->
com.android.internal.R.drawable.ic_zen_mode_type_unknown;
};
return requireNonNull(context.getDrawable(iconResIdFromType));
}
private static Drawable getMonochromeIconIfPresent(Drawable icon) {
// For created rules, the app should've provided a monochrome Drawable. However, implicit
// rules have the app's icon, which is not -- but might have a monochrome layer. Thus
// we choose it, if present.
if (icon instanceof AdaptiveIconDrawable adaptiveIcon) {
if (adaptiveIcon.getMonochrome() != null) {
// Wrap with negative inset => scale icon (inspired from BaseIconFactory)
return new InsetDrawable(adaptiveIcon.getMonochrome(),
-2.0f * AdaptiveIconDrawable.getExtraInsetFraction());
}
}
return icon;
}
}

View File

@@ -26,6 +26,8 @@ import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class InterruptionFilterPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {

View File

@@ -1,257 +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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
import static android.service.notification.ZenModeConfig.tryParseEventConditionId;
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import android.annotation.SuppressLint;
import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Objects;
/**
* Represents either an {@link AutomaticZenRule} or the manual DND rule in a unified way.
*
* <p>It also adapts other rule features that we don't want to expose in the UI, such as
* interruption filters other than {@code PRIORITY}, rules without specific icons, etc.
*/
class ZenMode {
private static final String TAG = "ZenMode";
static final String MANUAL_DND_MODE_ID = "manual_dnd";
// Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
new ZenPolicy.Builder()
.disallowAllSounds()
.allowAlarms(true)
.allowMedia(true)
.allowPriorityChannels(false)
.build();
// Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
private static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
new ZenPolicy.Builder()
.disallowAllSounds()
.hideAllVisualEffects()
.allowPriorityChannels(false)
.build();
private final String mId;
private AutomaticZenRule mRule;
private final boolean mIsActive;
private final boolean mIsManualDnd;
ZenMode(String id, AutomaticZenRule rule, boolean isActive) {
this(id, rule, isActive, false);
}
private ZenMode(String id, AutomaticZenRule rule, boolean isActive, boolean isManualDnd) {
mId = id;
mRule = rule;
mIsActive = isActive;
mIsManualDnd = isManualDnd;
}
static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
return new ZenMode(MANUAL_DND_MODE_ID, manualRule, isActive, true);
}
@NonNull
public String getId() {
return mId;
}
@NonNull
public AutomaticZenRule getRule() {
return mRule;
}
@NonNull
public ListenableFuture<Drawable> getIcon(@NonNull Context context,
@NonNull IconLoader iconLoader) {
if (mIsManualDnd) {
return Futures.immediateFuture(requireNonNull(
context.getDrawable(R.drawable.ic_do_not_disturb_on_24dp)));
}
return iconLoader.getIcon(context, mRule);
}
@NonNull
public ZenPolicy getPolicy() {
switch (mRule.getInterruptionFilter()) {
case INTERRUPTION_FILTER_PRIORITY:
case NotificationManager.INTERRUPTION_FILTER_ALL:
return requireNonNull(mRule.getZenPolicy());
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
return POLICY_INTERRUPTION_FILTER_ALARMS;
case NotificationManager.INTERRUPTION_FILTER_NONE:
return POLICY_INTERRUPTION_FILTER_NONE;
case NotificationManager.INTERRUPTION_FILTER_UNKNOWN:
default:
Log.wtf(TAG, "Rule " + mId + " with unexpected interruptionFilter "
+ mRule.getInterruptionFilter());
return requireNonNull(mRule.getZenPolicy());
}
}
/**
* Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the
* supplied policy. In some cases this involves conversions, so that the following call
* to {@link #getPolicy} might return a different policy from the one supplied here.
*/
@SuppressLint("WrongConstant")
public void setPolicy(@NonNull ZenPolicy policy) {
ZenPolicy currentPolicy = getPolicy();
if (currentPolicy.equals(policy)) {
return;
}
if (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALL) {
Log.wtf(TAG, "Able to change policy without filtering being enabled");
}
// If policy is customized from any of the "special" ones, make the rule PRIORITY.
if (mRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
mRule.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
}
mRule.setZenPolicy(policy);
}
@NonNull
public ZenDeviceEffects getDeviceEffects() {
return mRule.getDeviceEffects() != null
? mRule.getDeviceEffects()
: new ZenDeviceEffects.Builder().build();
}
public void setCustomModeConditionId(Context context, Uri conditionId) {
checkState(SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()),
"Trying to change condition of non-system-owned rule %s (to %s)",
mRule, conditionId);
Uri oldCondition = mRule.getConditionId();
mRule.setConditionId(conditionId);
ZenModeConfig.ScheduleInfo scheduleInfo = tryParseScheduleConditionId(conditionId);
if (scheduleInfo != null) {
mRule.setType(AutomaticZenRule.TYPE_SCHEDULE_TIME);
mRule.setOwner(ZenModeConfig.getScheduleConditionProvider());
mRule.setTriggerDescription(
getTriggerDescriptionForScheduleTime(context, scheduleInfo));
return;
}
ZenModeConfig.EventInfo eventInfo = tryParseEventConditionId(conditionId);
if (eventInfo != null) {
mRule.setType(AutomaticZenRule.TYPE_SCHEDULE_CALENDAR);
mRule.setOwner(ZenModeConfig.getEventConditionProvider());
mRule.setTriggerDescription(getTriggerDescriptionForScheduleEvent(context, eventInfo));
return;
}
if (ZenModeConfig.isValidCustomManualConditionId(conditionId)) {
mRule.setType(AutomaticZenRule.TYPE_OTHER);
mRule.setOwner(ZenModeConfig.getCustomManualConditionProvider());
mRule.setTriggerDescription("");
return;
}
Log.wtf(TAG, String.format(
"Changed condition of rule %s (%s -> %s) but cannot recognize which kind of "
+ "condition it was!",
mRule, oldCondition, conditionId));
}
public boolean canEditName() {
return !isManualDnd();
}
public boolean canEditIcon() {
return !isManualDnd();
}
public boolean canBeDeleted() {
return !mIsManualDnd;
}
public boolean isManualDnd() {
return mIsManualDnd;
}
public boolean isActive() {
return mIsActive;
}
public boolean isSystemOwned() {
return SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName());
}
@AutomaticZenRule.Type
public int getType() {
return mRule.getType();
}
@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof ZenMode other
&& mId.equals(other.mId)
&& mRule.equals(other.mRule)
&& mIsActive == other.mIsActive;
}
@Override
public int hashCode() {
return Objects.hash(mId, mRule, mIsActive);
}
@Override
public String toString() {
return mId + "(" + (mIsActive ? "active" : "inactive") + ") -> " + mRule;
}
}

View File

@@ -27,6 +27,8 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.ActionButtonsPreference;
class ZenModeActionsPreferenceController extends AbstractZenModePreferenceController {

View File

@@ -31,6 +31,8 @@ import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import java.util.ArrayList;
import java.util.HashMap;
@@ -128,11 +130,13 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
return appsBypassingDnd;
}
@VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks =
@VisibleForTesting
final ApplicationsState.Callbacks mAppSessionCallbacks =
new ApplicationsState.Callbacks() {
@Override
public void onRunningStateChanged(boolean running) { }
public void onRunningStateChanged(boolean running) {
}
@Override
public void onPackageListChanged() {
@@ -145,16 +149,20 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
}
@Override
public void onPackageIconChanged() { }
public void onPackageIconChanged() {
}
@Override
public void onPackageSizeChanged(String packageName) { }
public void onPackageSizeChanged(String packageName) {
}
@Override
public void onAllSizesComputed() { }
public void onAllSizesComputed() {
}
@Override
public void onLauncherInfoChanged() { }
public void onLauncherInfoChanged() {
}
@Override
public void onLoadEntriesCompleted() {

View File

@@ -31,6 +31,8 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
public class ZenModeAppsPreferenceController extends

View File

@@ -23,6 +23,8 @@ import android.widget.Button;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController {

View File

@@ -25,6 +25,8 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class ZenModeCallsLinkPreferenceController extends AbstractZenModePreferenceController {

View File

@@ -23,6 +23,9 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {

View File

@@ -25,6 +25,8 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceController {

View File

@@ -23,6 +23,9 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
/**
* Preference controller controlling whether a time schedule-based mode ends at the next alarm.
*/

View File

@@ -24,6 +24,7 @@ import android.content.Context;
import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.notification.modes.ZenMode;
import java.util.ArrayList;
import java.util.List;

View File

@@ -31,6 +31,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.notification.modes.ZenMode;
import java.util.List;

View File

@@ -25,6 +25,9 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.notification.modes.ZenIconLoader;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
class ZenModeHeaderController extends AbstractZenModePreferenceController {
@@ -62,7 +65,7 @@ class ZenModeHeaderController extends AbstractZenModePreferenceController {
}
FutureUtil.whenDone(
zenMode.getIcon(mContext, IconLoader.getInstance()),
zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon))
.done(/* rebindActions= */ false),
mContext.getMainExecutor());

View File

@@ -25,6 +25,9 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.notification.modes.ZenIconLoader;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenceController {
@@ -51,7 +54,7 @@ class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenc
}
FutureUtil.whenDone(
zenMode.getIcon(mContext, IconLoader.getInstance()),
zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon))
.done(/* rebindActions= */ false),
mContext.getMainExecutor());

View File

@@ -33,6 +33,8 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
import com.google.common.collect.ImmutableList;

View File

@@ -25,6 +25,8 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class ZenModeMessagesLinkPreferenceController extends AbstractZenModePreferenceController {
private final ZenModeSummaryHelper mSummaryHelper;

View File

@@ -26,6 +26,8 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceController {

View File

@@ -21,19 +21,21 @@ import android.service.notification.ZenPolicy;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settings.widget.DisabledCheckBoxPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {
@VisibleForTesting protected @ZenPolicy.VisualEffect int mEffect;
@VisibleForTesting
protected @ZenPolicy.VisualEffect int mEffect;
// if any of these effects are suppressed, this effect must be too
@VisibleForTesting protected @ZenPolicy.VisualEffect int[] mParentSuppressedEffects;
@VisibleForTesting
protected @ZenPolicy.VisualEffect int[] mParentSuppressedEffects;
public ZenModeNotifVisPreferenceController(Context context, String key,
@ZenPolicy.VisualEffect int visualEffect,

View File

@@ -26,6 +26,8 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
/**
* Preference with a link and summary about what other sounds can break through the mode

View File

@@ -28,6 +28,9 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class ZenModeOtherPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {

View File

@@ -26,6 +26,8 @@ import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
/**
* Preference with a link and summary about what calls and messages can break through the mode

View File

@@ -46,6 +46,8 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.app.ConversationListSettings;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.ArrayList;

View File

@@ -26,6 +26,8 @@ import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {

View File

@@ -31,6 +31,8 @@ import androidx.preference.PreferenceCategory;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import java.util.ArrayList;
import java.util.Arrays;

View File

@@ -31,6 +31,8 @@ import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
import java.text.SimpleDateFormat;

View File

@@ -29,6 +29,8 @@ import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
/**
* Preference controller for the link to an individual mode's configuration page.

View File

@@ -48,6 +48,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import java.util.ArrayList;
import java.util.Arrays;
@@ -193,7 +194,7 @@ class ZenModeSummaryHelper {
enabledEffects.add(getBlockedEffectsSummary(zenMode));
isFirst = false;
}
ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects();
ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects();
if (currEffects != null) {
if (currEffects.shouldDisplayGrayscale()) {
if (isFirst) {

View File

@@ -1,207 +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.modes;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.content.Context;
import android.net.Uri;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.util.Log;
import com.android.settings.R;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Class used for Settings-NMS interactions related to Mode management.
*
* <p>This class converts {@link AutomaticZenRule} instances, as well as the manual zen mode,
* into the unified {@link ZenMode} format.
*/
class ZenModesBackend {
private static final String TAG = "ZenModeBackend";
@Nullable // Until first usage
private static ZenModesBackend sInstance;
private final NotificationManager mNotificationManager;
private final Context mContext;
static ZenModesBackend getInstance(Context context) {
if (sInstance == null) {
sInstance = new ZenModesBackend(context.getApplicationContext());
}
return sInstance;
}
ZenModesBackend(Context context) {
mContext = context;
mNotificationManager = context.getSystemService(NotificationManager.class);
}
List<ZenMode> getModes() {
ArrayList<ZenMode> modes = new ArrayList<>();
ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();
modes.add(getManualDndMode(currentConfig));
Map<String, AutomaticZenRule> zenRules = mNotificationManager.getAutomaticZenRules();
for (Map.Entry<String, AutomaticZenRule> zenRuleEntry : zenRules.entrySet()) {
String ruleId = zenRuleEntry.getKey();
modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(),
isRuleActive(ruleId, currentConfig)));
}
modes.sort((l, r) -> {
if (l.isManualDnd()) {
return -1;
} else if (r.isManualDnd()) {
return 1;
}
return l.getRule().getName().compareTo(r.getRule().getName());
});
return modes;
}
@Nullable
ZenMode getMode(String id) {
ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();
if (ZenMode.MANUAL_DND_MODE_ID.equals(id)) {
return getManualDndMode(currentConfig);
} else {
AutomaticZenRule rule = mNotificationManager.getAutomaticZenRule(id);
if (rule == null) {
return null;
}
return new ZenMode(id, rule, isRuleActive(id, currentConfig));
}
}
private ZenMode getManualDndMode(ZenModeConfig config) {
ZenModeConfig.ZenRule manualRule = config.manualRule;
// TODO: b/333682392 - Replace with final strings for name & trigger description
AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder(
mContext.getString(R.string.zen_mode_settings_title), manualRule.conditionId)
.setType(manualRule.type)
.setZenPolicy(manualRule.zenPolicy)
.setDeviceEffects(manualRule.zenDeviceEffects)
.setManualInvocationAllowed(manualRule.allowManualInvocation)
.setConfigurationActivity(null) // No further settings
.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
.build();
return ZenMode.manualDndMode(manualDndRule, config != null && config.isManualActive());
}
private static boolean isRuleActive(String id, ZenModeConfig config) {
if (config == null) {
// shouldn't happen if the config is coming from NM, but be safe
return false;
}
ZenModeConfig.ZenRule configRule = config.automaticRules.get(id);
return configRule != null && configRule.isAutomaticActive();
}
void updateMode(ZenMode mode) {
if (mode.isManualDnd()) {
try {
NotificationManager.Policy dndPolicy =
new ZenModeConfig().toNotificationPolicy(mode.getPolicy());
mNotificationManager.setNotificationPolicy(dndPolicy, /* fromUser= */ true);
mNotificationManager.setManualZenRuleDeviceEffects(
mode.getRule().getDeviceEffects());
} catch (Exception e) {
Log.w(TAG, "Error updating manual mode", e);
}
} else {
mNotificationManager.updateAutomaticZenRule(mode.getId(), mode.getRule(),
/* fromUser= */ true);
}
}
void activateMode(ZenMode mode, @Nullable Duration forDuration) {
if (mode.isManualDnd()) {
Uri durationConditionId = null;
if (forDuration != null) {
durationConditionId = ZenModeConfig.toTimeCondition(mContext,
(int) forDuration.toMinutes(), ActivityManager.getCurrentUser(), true).id;
}
mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
durationConditionId, TAG, /* fromUser= */ true);
} else {
if (forDuration != null) {
throw new IllegalArgumentException(
"Only the manual DND mode can be activated for a specific duration");
}
mNotificationManager.setAutomaticZenRuleState(mode.getId(),
new Condition(mode.getRule().getConditionId(), "", Condition.STATE_TRUE,
Condition.SOURCE_USER_ACTION));
}
}
void deactivateMode(ZenMode mode) {
if (mode.isManualDnd()) {
// When calling with fromUser=true this will not snooze other modes.
mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
/* fromUser= */ true);
} else {
// TODO: b/333527800 - This should (potentially) snooze the rule if it was active.
mNotificationManager.setAutomaticZenRuleState(mode.getId(),
new Condition(mode.getRule().getConditionId(), "", Condition.STATE_FALSE,
Condition.SOURCE_USER_ACTION));
}
}
void removeMode(ZenMode mode) {
if (!mode.canBeDeleted()) {
throw new IllegalArgumentException("Mode " + mode + " cannot be deleted!");
}
mNotificationManager.removeAutomaticZenRule(mode.getId(), /* fromUser= */ true);
}
/**
* Creates a new custom mode with the provided {@code name}. The mode will be "manual" (i.e.
* not have a schedule), this can be later updated by the user in the mode settings page.
*
* @return the created mode. Only {@code null} if creation failed due to an internal error
*/
@Nullable
ZenMode addCustomMode(String name) {
AutomaticZenRule rule = new AutomaticZenRule.Builder(name,
ZenModeConfig.toCustomManualConditionId())
.setPackage(ZenModeConfig.getCustomManualConditionProvider().getPackageName())
.setType(AutomaticZenRule.TYPE_OTHER)
.setOwner(ZenModeConfig.getCustomManualConditionProvider())
.setManualInvocationAllowed(true)
.build();
String ruleId = mNotificationManager.addAutomaticZenRule(rule);
return getMode(ruleId);
}
}

View File

@@ -27,6 +27,7 @@ import android.provider.Settings.Global;
import android.util.Log;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settingslib.notification.modes.ZenModesBackend;
/**
* Base class for all Settings pages controlling Modes behavior.

View File

@@ -22,6 +22,8 @@ import androidx.preference.Preference;
import com.android.settings.utils.ZenServiceListing;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import java.util.Random;

View File

@@ -29,6 +29,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.utils.ManagedServiceSettings;
import com.android.settings.utils.ZenServiceListing;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.search.SearchIndexable;
import com.google.common.collect.ImmutableList;

View File

@@ -18,6 +18,8 @@ package com.android.settings.notification.modes;
import android.content.Context;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.notification.modes.ZenIconLoader;
import com.android.settingslib.notification.modes.ZenMode;
/**
* Preference representing a single mode item on the modes aggregator page. Clicking on this
@@ -46,7 +48,7 @@ class ZenModesListItemPreference extends RestrictedPreference {
setIconSize(ICON_SIZE_SMALL);
FutureUtil.whenDone(
mZenMode.getIcon(mContext, IconLoader.getInstance()),
mZenMode.getIcon(mContext, ZenIconLoader.getInstance()),
icon -> setIcon(IconUtil.applyTint(mContext, icon)),
mContext.getMainExecutor());
}

View File

@@ -27,6 +27,8 @@ import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.search.SearchIndexableRaw;
import java.util.HashMap;

View File

@@ -116,7 +116,7 @@ public class ZenModeBackend {
ActivityManager.getCurrentUser(), true).id;
if (android.app.Flags.modesApi()) {
mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
conditionId, TAG, /* fromUser= */ true);
conditionId, TAG, /* fromUser= */ true);
} else {
mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
conditionId, TAG);
@@ -241,12 +241,14 @@ public class ZenModeBackend {
}
savePolicy(getNewDefaultPriorityCategories(allowSenders, category),
priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects,
priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects,
mPolicy.priorityConversationSenders);
if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow" +
stringCategory + "=" + allowSenders + " allow" + stringCategory + "From="
+ ZenModeConfig.sourceToString(allowSendersFrom));
if (ZenModeSettingsBase.DEBUG) {
Log.d(TAG, "onPrefChange allow"
+ stringCategory + "=" + allowSenders + " allow" + stringCategory + "From="
+ ZenModeConfig.sourceToString(allowSendersFrom));
}
}
protected void saveConversationSenders(int val) {
@@ -280,7 +282,7 @@ public class ZenModeBackend {
switch (contactType) {
case ZenPolicy.PEOPLE_TYPE_ANYONE:
return ZEN_MODE_FROM_ANYONE;
case ZenPolicy.PEOPLE_TYPE_CONTACTS:
case ZenPolicy.PEOPLE_TYPE_CONTACTS:
return ZEN_MODE_FROM_CONTACTS;
case ZenPolicy.PEOPLE_TYPE_STARRED:
return ZEN_MODE_FROM_STARRED;
@@ -308,7 +310,7 @@ public class ZenModeBackend {
switch (setting) {
case ZenPolicy.PEOPLE_TYPE_ANYONE:
return NotificationManager.Policy.PRIORITY_SENDERS_ANY;
case ZenPolicy.PEOPLE_TYPE_CONTACTS:
case ZenPolicy.PEOPLE_TYPE_CONTACTS:
return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
case ZenPolicy.PEOPLE_TYPE_STARRED:
return NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
@@ -321,7 +323,7 @@ public class ZenModeBackend {
protected int getAlarmsTotalSilencePeopleSummary(int category) {
if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
return R.string.zen_mode_none_messages;
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
return R.string.zen_mode_none_calls;
} else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) {
return R.string.zen_mode_from_no_conversations;
@@ -470,8 +472,8 @@ public class ZenModeBackend {
if (cursor != null && cursor.moveToFirst()) {
do {
String contact = cursor.getString(0);
starredContacts.add(contact != null ? contact :
mContext.getString(R.string.zen_mode_starred_contacts_empty_name));
int emptyNameId = R.string.zen_mode_starred_contacts_empty_name;
starredContacts.add(contact != null ? contact : mContext.getString(emptyNameId));
} while (cursor.moveToNext());
}

View File

@@ -132,8 +132,8 @@ public class ZenModeSliceBuilder {
final Uri contentUri = new Uri.Builder().appendPath(ZEN_MODE_SLICE_KEY).build();
final String screenTitle = context.getText(R.string.zen_mode_settings_title).toString();
return SliceBuilderUtils.buildSearchResultPageIntent(context,
ZenModeSettings.class.getName(), ZEN_MODE_SLICE_KEY, screenTitle,
SettingsEnums.NOTIFICATION_ZEN_MODE, R.string.menu_key_notifications)
ZenModeSettings.class.getName(), ZEN_MODE_SLICE_KEY, screenTitle,
SettingsEnums.NOTIFICATION_ZEN_MODE, R.string.menu_key_notifications)
.setClassName(context.getPackageName(), SubSettings.class.getName())
.setData(contentUri);
}

View File

@@ -1,91 +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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.service.notification.ZenPolicy;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class IconLoaderTest {
private Context mContext;
private IconLoader mLoader;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mLoader = new IconLoader(MoreExecutors.newDirectExecutorService());
}
@Test
public void getIcon_systemOwnedRuleWithIcon_loads() throws Exception {
AutomaticZenRule systemRule = newRuleBuilder()
.setPackage("android")
.setIconResId(android.R.drawable.ic_media_play)
.build();
ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, systemRule);
assertThat(loadFuture.isDone()).isTrue();
assertThat(loadFuture.get()).isNotNull();
}
@Test
public void getIcon_ruleWithoutSpecificIcon_loadsFallback() throws Exception {
AutomaticZenRule rule = newRuleBuilder()
.setType(AutomaticZenRule.TYPE_DRIVING)
.setPackage("com.blah")
.build();
ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
assertThat(loadFuture.isDone()).isTrue();
assertThat(loadFuture.get()).isNotNull();
}
@Test
public void getIcon_ruleWithAppIconWithLoadFailure_loadsFallback() throws Exception {
AutomaticZenRule rule = newRuleBuilder()
.setType(AutomaticZenRule.TYPE_DRIVING)
.setPackage("com.blah")
.setIconResId(-123456)
.build();
ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
assertThat(loadFuture.get()).isNotNull();
}
private static AutomaticZenRule.Builder newRuleBuilder() {
return new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().build());
}
}

View File

@@ -18,13 +18,10 @@ package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static android.service.notification.ZenPolicy.STATE_DISALLOW;
import static android.service.notification.ZenPolicy.STATE_UNSET;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -36,9 +33,11 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -45,6 +45,8 @@ import androidx.preference.Preference;
import com.android.settings.SettingsActivity;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.Before;

View File

@@ -16,8 +16,6 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_NONE;
@@ -41,6 +39,8 @@ import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.Before;

View File

@@ -34,6 +34,8 @@ import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import android.widget.Button;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;

View File

@@ -32,6 +32,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -17,9 +17,9 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static android.service.notification.ZenPolicy.STATE_UNSET;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -31,7 +31,12 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -32,6 +32,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -28,6 +28,9 @@ import android.service.notification.ZenModeConfig;
import androidx.preference.TwoStatePreference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@@ -33,6 +33,8 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.LayoutPreference;
import com.google.common.collect.ImmutableList;

View File

@@ -32,6 +32,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -32,6 +32,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -43,6 +43,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -32,6 +32,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -35,6 +35,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -32,6 +32,9 @@ import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -54,6 +54,8 @@ import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.Before;

View File

@@ -19,10 +19,11 @@ package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static android.service.notification.ZenPolicy.STATE_DISALLOW;
import static android.service.notification.ZenPolicy.STATE_UNSET;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -33,7 +34,12 @@ import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import androidx.preference.TwoStatePreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -41,6 +41,9 @@ import androidx.preference.DropDownPreference;
import androidx.preference.PreferenceCategory;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -38,6 +38,8 @@ import androidx.fragment.app.Fragment;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;

View File

@@ -46,6 +46,8 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Rule;

View File

@@ -1,117 +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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
import android.net.Uri;
import android.service.notification.ZenPolicy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ZenModeTest {
private static final ZenPolicy ZEN_POLICY = new ZenPolicy.Builder().allowAllSounds().build();
private static final AutomaticZenRule ZEN_RULE =
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(ZEN_POLICY)
.build();
@Test
public void testBasicMethods() {
ZenMode zenMode = new ZenMode("id", ZEN_RULE, true);
assertThat(zenMode.getId()).isEqualTo("id");
assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE);
assertThat(zenMode.isManualDnd()).isFalse();
assertThat(zenMode.canBeDeleted()).isTrue();
assertThat(zenMode.isActive()).isTrue();
ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false);
assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
assertThat(manualMode.isManualDnd()).isTrue();
assertThat(manualMode.canBeDeleted()).isFalse();
assertThat(manualMode.isActive()).isFalse();
}
@Test
public void getPolicy_interruptionFilterPriority_returnsZenPolicy() {
ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(ZEN_POLICY)
.build(), false);
assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
}
@Test
public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() {
ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
.setZenPolicy(ZEN_POLICY) // should be ignored
.build(), false);
assertThat(zenMode.getPolicy()).isEqualTo(
new ZenPolicy.Builder()
.disallowAllSounds()
.allowAlarms(true)
.allowMedia(true)
.allowPriorityChannels(false)
.build());
}
@Test
public void getPolicy_interruptionFilterNone_returnsPolicyAllowingNothing() {
ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setInterruptionFilter(INTERRUPTION_FILTER_NONE)
.setZenPolicy(ZEN_POLICY) // should be ignored
.build(), false);
assertThat(zenMode.getPolicy()).isEqualTo(
new ZenPolicy.Builder()
.disallowAllSounds()
.hideAllVisualEffects()
.allowPriorityChannels(false)
.build());
}
@Test
public void setPolicy_setsInterruptionFilterPriority() {
ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
.build(), false);
zenMode.setPolicy(ZEN_POLICY);
assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(
INTERRUPTION_FILTER_PRIORITY);
assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY);
}
}

View File

@@ -1,363 +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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.SOURCE_UNKNOWN;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenAdapters;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import com.android.settings.R;
import com.google.common.collect.ImmutableMap;
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 org.robolectric.shadows.ShadowApplication;
import java.time.Duration;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public class ZenModesBackendTest {
private static final String ZEN_RULE_ID = "rule";
private static final AutomaticZenRule ZEN_RULE =
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build();
private static final AutomaticZenRule MANUAL_DND_RULE =
new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build();
@Mock
private NotificationManager mNm;
private Context mContext;
private ZenModesBackend mBackend;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
// Helper methods to add active/inactive rule state to a config. Returns a copy.
private ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) {
ZenModeConfig out = base.copy();
if (active) {
out.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
out.manualRule.condition =
new Condition(out.manualRule.conditionId, "", STATE_TRUE, SOURCE_UNKNOWN);
} else {
out.manualRule.zenMode = ZEN_MODE_OFF;
out.manualRule.condition =
new Condition(out.manualRule.conditionId, "", STATE_FALSE, SOURCE_UNKNOWN);
}
return out;
}
private ZenModeConfig configWithRule(ZenModeConfig base, String ruleId, AutomaticZenRule rule,
boolean active) {
ZenModeConfig out = base.copy();
// Note that there are many other fields of zenRule, but here we only set the ones
// relevant to determining whether or not it is active.
ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule();
zenRule.pkg = "package";
zenRule.enabled = active;
zenRule.snoozing = false;
zenRule.condition = new Condition(rule.getConditionId(), "",
active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
Condition.SOURCE_USER_ACTION);
out.automaticRules.put(ruleId, zenRule);
return out;
}
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
mContext = RuntimeEnvironment.application;
mBackend = new ZenModesBackend(mContext);
// Default catch-all case with no data. This isn't realistic, but tests below that rely
// on the config to get data on rules active will create those individually.
when(mNm.getZenModeConfig()).thenReturn(new ZenModeConfig());
}
@Test
public void getModes_containsManualDndAndZenRules() {
AutomaticZenRule rule2 = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
.build();
Policy dndPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
when(mNm.getAutomaticZenRules()).thenReturn(
ImmutableMap.of("rule1", ZEN_RULE, "rule2", rule2));
ZenModeConfig config = new ZenModeConfig();
config.applyNotificationPolicy(dndPolicy);
assertThat(config.manualRule.zenPolicy.getPriorityCategoryAlarms()).isEqualTo(STATE_ALLOW);
when(mNm.getZenModeConfig()).thenReturn(config);
List<ZenMode> modes = mBackend.getModes();
// all modes exist, but none of them are currently active
assertThat(modes).containsExactly(
ZenMode.manualDndMode(
new AutomaticZenRule.Builder(
mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
.setType(AutomaticZenRule.TYPE_OTHER)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
.setManualInvocationAllowed(true)
.build(),
false),
new ZenMode("rule2", rule2, false),
new ZenMode("rule1", ZEN_RULE, false))
.inOrder();
}
@Test
public void getMode_manualDnd_returnsMode() {
Policy dndPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
ZenModeConfig config = new ZenModeConfig();
config.applyNotificationPolicy(dndPolicy);
when(mNm.getZenModeConfig()).thenReturn(config);
ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
assertThat(mode).isEqualTo(
ZenMode.manualDndMode(
new AutomaticZenRule.Builder(
mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
.setType(AutomaticZenRule.TYPE_OTHER)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
.setManualInvocationAllowed(true)
.build(), false));
}
@Test
public void getMode_zenRule_returnsMode() {
when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
assertThat(mode).isEqualTo(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false));
}
@Test
public void getMode_missingRule_returnsNull() {
when(mNm.getAutomaticZenRule(any())).thenReturn(null);
ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
assertThat(mode).isNull();
verify(mNm).getAutomaticZenRule(eq(ZEN_RULE_ID));
}
@Test
public void getMode_manualDnd_returnsCorrectActiveState() {
// Set up a base config with an active rule to make sure we're looking at the correct info
ZenModeConfig configWithActiveRule = configWithRule(new ZenModeConfig(), ZEN_RULE_ID,
ZEN_RULE, true);
// Equivalent to disallowAllSounds()
Policy dndPolicy = new Policy(0, 0, 0);
configWithActiveRule.applyNotificationPolicy(dndPolicy);
when(mNm.getZenModeConfig()).thenReturn(configWithActiveRule);
ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
// By default, manual rule is inactive
assertThat(mode.isActive()).isFalse();
// Now the returned config will represent the manual rule being active
when(mNm.getZenModeConfig()).thenReturn(configWithManualRule(configWithActiveRule, true));
ZenMode activeMode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
assertThat(activeMode.isActive()).isTrue();
}
@Test
public void getMode_zenRule_returnsCorrectActiveState() {
// Set up a base config that has an active manual rule and "rule2", to make sure we're
// looking at the correct rule's info.
ZenModeConfig configWithActiveRules = configWithRule(
configWithManualRule(new ZenModeConfig(), true), // active manual rule
"rule2", ZEN_RULE, true); // active rule 2
when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
when(mNm.getZenModeConfig()).thenReturn(
configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, false));
// Round 1: the current config should indicate that the rule is not active
ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
assertThat(mode.isActive()).isFalse();
when(mNm.getZenModeConfig()).thenReturn(
configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, true));
ZenMode activeMode = mBackend.getMode(ZEN_RULE_ID);
assertThat(activeMode.isActive()).isTrue();
}
@Test
public void updateMode_manualDnd_setsDeviceEffects() throws Exception {
ZenMode manualDnd = ZenMode.manualDndMode(
new AutomaticZenRule.Builder("DND", Uri.EMPTY)
.setZenPolicy(new ZenPolicy())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.build())
.build(), false);
mBackend.updateMode(manualDnd);
verify(mNm).setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.build());
}
@Test
public void updateMode_manualDnd_setsNotificationPolicy() {
ZenMode manualDnd = ZenMode.manualDndMode(
new AutomaticZenRule.Builder("DND", Uri.EMPTY)
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build(), false);
mBackend.updateMode(manualDnd);
verify(mNm).setNotificationPolicy(eq(new ZenModeConfig().toNotificationPolicy(
new ZenPolicy.Builder().allowAllSounds().build())), eq(true));
}
@Test
public void updateMode_zenRule_updatesRule() {
ZenMode ruleMode = new ZenMode("rule", ZEN_RULE, false);
mBackend.updateMode(ruleMode);
verify(mNm).updateAutomaticZenRule(eq("rule"), eq(ZEN_RULE), eq(true));
}
@Test
public void activateMode_manualDnd_setsZenModeImportant() {
mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), null);
verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
any(), eq(true));
}
@Test
public void activateMode_manualDndWithDuration_setsZenModeImportantWithCondition() {
mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false),
Duration.ofMinutes(30));
verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS),
eq(ZenModeConfig.toTimeCondition(mContext, 30, 0, true).id),
any(),
eq(true));
}
@Test
public void activateMode_zenRule_setsRuleStateActive() {
mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false), null);
verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID),
eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_TRUE,
Condition.SOURCE_USER_ACTION)));
}
@Test
public void activateMode_zenRuleWithDuration_fails() {
assertThrows(IllegalArgumentException.class,
() -> mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false),
Duration.ofMinutes(30)));
}
@Test
public void deactivateMode_manualDnd_setsZenModeOff() {
mBackend.deactivateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, true));
verify(mNm).setZenMode(eq(ZEN_MODE_OFF), eq(null), any(), eq(true));
}
@Test
public void deactivateMode_zenRule_setsRuleStateInactive() {
mBackend.deactivateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false));
verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID),
eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_FALSE,
Condition.SOURCE_USER_ACTION)));
}
@Test
public void removeMode_zenRule_deletesRule() {
mBackend.removeMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false));
verify(mNm).removeAutomaticZenRule(ZEN_RULE_ID, true);
}
@Test
public void removeMode_manualDnd_fails() {
assertThrows(IllegalArgumentException.class,
() -> mBackend.removeMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false)));
}
}

View File

@@ -37,6 +37,8 @@ import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import com.android.settingslib.search.SearchIndexableRaw;
import com.google.common.collect.ImmutableList;

View File

@@ -31,6 +31,8 @@ import android.net.Uri;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import com.android.settingslib.notification.modes.ZenMode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +42,6 @@ import org.robolectric.RuntimeEnvironment;
import java.util.LinkedHashSet;
import java.util.Set;
@RunWith(RobolectricTestRunner.class)
public class ZenModesSummaryHelperTest {
private Context mContext;