Design refresh for modes that don't filter notifications

And fix a crash noticed in ZenModeAppsLinkPreferenceController

Test: atest com/android/settings/notification/modes
Fixes: 308820027
Flag: android.app.modes_ui
Change-Id: I0cfe4e10ca7ff97dac3b3b8756cc36f4d6f91ea2
This commit is contained in:
Julia Reynolds
2024-06-17 09:15:57 -04:00
parent 78a0662272
commit 3b62c23310
18 changed files with 281 additions and 276 deletions

View File

@@ -0,0 +1,62 @@
/*
* 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 android.content.Context;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
class InterruptionFilterPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {
public InterruptionFilterPreferenceController(Context context, String key,
ZenModesBackend backend) {
super(context, key, backend);
}
@Override
public boolean isAvailable(ZenMode zenMode) {
return !zenMode.isManualDnd();
}
@Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
boolean filteringNotifications = zenMode.getRule().getInterruptionFilter()
!= INTERRUPTION_FILTER_ALL;
((TwoStatePreference) preference).setChecked(filteringNotifications);
preference.setSummary(filteringNotifications ? "" :
mContext.getResources().getString(R.string.mode_no_notification_filter));
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean filterNotifications = ((Boolean) newValue);
return saveMode(zenMode -> {
zenMode.getRule().setInterruptionFilter(filterNotifications
? INTERRUPTION_FILTER_PRIORITY
: INTERRUPTION_FILTER_ALL);
return zenMode;
});
}
}

View File

@@ -59,26 +59,8 @@ class ZenMode {
private static final String TAG = "ZenMode";
/**
* Additional value for the {@code @ZenPolicy.ChannelType} enumeration that indicates that all
* channels can bypass DND when this policy is active.
*
* <p>This value shouldn't be used on "real" ZenPolicy objects sent to or returned from
* {@link android.app.NotificationManager}; it's a way of representing rules with interruption
* filter = {@link NotificationManager#INTERRUPTION_FILTER_ALL} in the UI.
*/
public static final int CHANNEL_POLICY_ALL = -1;
static final String MANUAL_DND_MODE_ID = "manual_dnd";
@SuppressLint("WrongConstant")
private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALL =
new ZenPolicy.Builder()
.allowChannels(CHANNEL_POLICY_ALL)
.allowAllSounds()
.showAllVisualEffects()
.build();
// Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
new ZenPolicy.Builder()
@@ -141,10 +123,8 @@ class ZenMode {
public ZenPolicy getPolicy() {
switch (mRule.getInterruptionFilter()) {
case INTERRUPTION_FILTER_PRIORITY:
return requireNonNull(mRule.getZenPolicy());
case NotificationManager.INTERRUPTION_FILTER_ALL:
return POLICY_INTERRUPTION_FILTER_ALL;
return requireNonNull(mRule.getZenPolicy());
case NotificationManager.INTERRUPTION_FILTER_ALARMS:
return POLICY_INTERRUPTION_FILTER_ALARMS;
@@ -172,31 +152,8 @@ class ZenMode {
return;
}
// A policy with CHANNEL_POLICY_ALL is only a UI representation of the
// INTERRUPTION_FILTER_ALL filter. Thus, switching to or away to this value only updates
// the filter, discarding the rest of the supplied policy.
if (policy.getAllowedChannels() == CHANNEL_POLICY_ALL
&& currentPolicy.getAllowedChannels() != CHANNEL_POLICY_ALL) {
if (mIsManualDnd) {
throw new IllegalArgumentException("Manual DND cannot have CHANNEL_POLICY_ALL");
}
mRule.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
// Preserve the existing policy, e.g. if the user goes PRIORITY -> ALL -> PRIORITY that
// shouldn't discard all other policy customizations. The existing policy will be a
// synthetic one if the rule originally had filter NONE or ALARMS_ONLY and that's fine.
if (mRule.getZenPolicy() == null) {
mRule.setZenPolicy(currentPolicy);
}
return;
} else if (policy.getAllowedChannels() != CHANNEL_POLICY_ALL
&& currentPolicy.getAllowedChannels() == CHANNEL_POLICY_ALL) {
mRule.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
// Go back to whatever policy the rule had before, unless the rule never had one, in
// which case we use the supplied policy (which we know has a valid allowedChannels).
if (mRule.getZenPolicy() == null) {
mRule.setZenPolicy(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.

View File

@@ -37,10 +37,6 @@ public class ZenModeAppsFragment extends ZenModeFragmentBase {
context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend));
controllers.add(new ZenModeAppsPreferenceController(
context, ZenModeAppsPreferenceController.KEY_NONE, mBackend));
// TODO: b/308819928 - The manual DND mode cannot have the ALL type;
// unify the controllers into one and only create a preference if isManualDnd is false.
controllers.add(new ZenModeAppsPreferenceController(
context, ZenModeAppsPreferenceController.KEY_ALL, mBackend));
return controllers;
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
@@ -45,10 +47,12 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
private static final String TAG = "ZenModeAppsLinkPreferenceController";
private final ZenModeSummaryHelper mSummaryHelper;
private final ApplicationsState mApplicationsState;
private ApplicationsState.Session mAppSession;
private final ZenHelperBackend mHelperBackend;
private ZenMode mZenMode;
private Preference mPreference;
private final Fragment mHost;
ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
ApplicationsState applicationsState, ZenModesBackend backend,
@@ -56,9 +60,13 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
super(context, key, backend);
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
mHelperBackend = helperBackend;
if (applicationsState != null && host != null) {
mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
}
mApplicationsState = applicationsState;
mHost = host;
}
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override
@@ -73,6 +81,9 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
.toIntent());
mZenMode = zenMode;
mPreference = preference;
if (mApplicationsState != null && mHost != null) {
mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mHost.getLifecycle());
}
triggerUpdateAppsBypassingDndSummaryText();
}

View File

@@ -38,11 +38,9 @@ public class ZenModeAppsPreferenceController extends
static final String KEY_PRIORITY = "zen_mode_apps_priority";
static final String KEY_NONE = "zen_mode_apps_none";
static final String KEY_ALL = "zen_mode_apps_all";
String mModeId;
public ZenModeAppsPreferenceController(@NonNull Context context,
@NonNull String key, @Nullable ZenModesBackend backend) {
super(context, key, backend);
@@ -79,13 +77,6 @@ public class ZenModeAppsPreferenceController extends
== ZenPolicy.CHANNEL_POLICY_NONE;
pref.setChecked(policy_none);
break;
case KEY_ALL:
// A UI-only setting; the underlying policy never actually has this value,
// but ZenMode acts as though it does for the sake of UI consistency.
boolean policy_all = zenMode.getPolicy().getAllowedChannels()
== ZenMode.CHANNEL_POLICY_ALL;
pref.setChecked(policy_all);
break;
}
}
@@ -96,8 +87,6 @@ public class ZenModeAppsPreferenceController extends
return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY));
case KEY_NONE:
return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE));
case KEY_ALL:
return savePolicy(p -> p.allowChannels(ZenMode.CHANNEL_POLICY_ALL));
}
return true;
}

View File

@@ -53,6 +53,8 @@ public class ZenModeFragment extends ZenModeFragmentBase {
context, "mode_display_settings", mBackend, mHelperBackend));
prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context,
"zen_automatic_trigger_category", this, mBackend));
prefControllers.add(new InterruptionFilterPreferenceController(
context, "allow_filtering", mBackend));
return prefControllers;
}

View File

@@ -120,10 +120,6 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
}
for (List<AbstractPreferenceController> list : getPreferenceControllers()) {
for (AbstractPreferenceController controller : list) {
if (!controller.isAvailable()) {
continue;
}
try {
// Find preference associated with controller
final String key = controller.getPreferenceKey();
@@ -137,6 +133,7 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
String.format("Cannot find preference with key %s in Controller %s",
key, controller.getClass().getSimpleName()));
}
controller.displayPreference(screen);
} catch (ClassCastException e) {
// Skip any controllers that aren't AbstractZenModePreferenceController.
Log.d(TAG, "Could not cast: " + controller.getClass().getSimpleName());

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
@@ -36,6 +38,11 @@ class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceC
mSummaryBuilder = new ZenModeSummaryHelper(context, helperBackend);
}
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle();

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
@@ -39,6 +41,11 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
}
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle();

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
@@ -39,6 +41,11 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
}
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle();

View File

@@ -15,6 +15,7 @@
*/
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
@@ -187,7 +188,8 @@ class ZenModeSummaryHelper {
String getDisplayEffectsSummary(ZenMode zenMode) {
boolean isFirst = true;
List<String> enabledEffects = new ArrayList<>();
if (!zenMode.getPolicy().shouldShowAllVisualEffects()) {
if (!zenMode.getPolicy().shouldShowAllVisualEffects()
&& zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
enabledEffects.add(getBlockedEffectsSummary(zenMode));
isFirst = false;
}
@@ -411,8 +413,6 @@ class ZenModeSummaryHelper {
return formatAppsList(appsBypassing);
} else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
} else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) {
return mContext.getResources().getString(R.string.zen_mode_apps_all_apps);
}
return "";
}