Extracts heads-up/pulsing logic from NotificationEntryManager.
This change introduces the NotificationInterruptionStateProvider
component, which contains all the logic around whether a notification
should heads-up or pulse previously contained in
NotificationEntryManager.
We also introduce the NotificationFilter component which extracts logic
about when to filter notifications from NotificationData, in order to
break a circular dependency that would otherwise be introduced. As part
of this, some additional fields from the notification ranking map are
denormalized on to the NotificationData.Entry object.
Test: atest SystemUITests, manually
Change-Id: Ic61edca966a3c3e0b01f1a6a9e7ce79c8701da4e
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 445d156..6b4d07a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,6 +56,8 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -256,6 +258,8 @@
@Inject Lazy<NotificationLogger> mNotificationLogger;
@Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
@Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
+ @Inject Lazy<NotificationFilter> mNotificationFilter;
+ @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -425,6 +429,9 @@
mProviders.put(NotificationViewHierarchyManager.class,
mNotificationViewHierarchyManager::get);
mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
+ mProviders.put(NotificationFilter.class, mNotificationFilter::get);
+ mProviders.put(NotificationInterruptionStateProvider.class,
+ mNotificationInterruptionStateProvider::get);
mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 7689dfc..384a14c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -198,6 +199,13 @@
return new NotificationListener(context);
}
+ @Singleton
+ @Provides
+ public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+ Context context) {
+ return new NotificationInterruptionStateProvider(context);
+ }
+
@Module
protected static class ContextHolder {
private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 2524747..017cda7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -133,7 +133,6 @@
mAlwaysExpandNonGroupedNotification =
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
- mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ef7e0fe..433a994 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -27,20 +27,15 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import android.Manifest;
import android.annotation.NonNull;
-import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -58,17 +53,13 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.PrintWriter;
@@ -83,14 +74,13 @@
*/
public class NotificationData {
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
/**
* These dependencies are late init-ed
*/
private KeyguardEnvironment mEnvironment;
- private ShadeController mShadeController;
private NotificationMediaManager mMediaManager;
- private ForegroundServiceController mFsc;
- private NotificationLockscreenUserManager mUserManager;
private HeadsUpManager mHeadsUpManager;
@@ -120,6 +110,9 @@
@NonNull
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
public CharSequence[] smartReplies = new CharSequence[0];
+ @VisibleForTesting
+ public int suppressedVisualEffects;
+ public boolean suspended;
private Entry parent; // our parent (if we're in a group)
private ArrayList<Entry> children = new ArrayList<Entry>();
@@ -183,6 +176,8 @@
smartReplies = ranking.getSmartReplies() == null
? new CharSequence[0]
: ranking.getSmartReplies().toArray(new CharSequence[0]);
+ suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+ suspended = ranking.isSuspended();
}
public void setInterruption() {
@@ -625,6 +620,71 @@
if (row == null) return true;
return row.canViewBeDismissed();
}
+
+ boolean isExemptFromDndVisualSuppression() {
+ if (isNotificationBlockedByPolicy(notification.getNotification())) {
+ return false;
+ }
+
+ if ((notification.getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
+ }
+ if (notification.getNotification().isMediaNotification()) {
+ return true;
+ }
+ if (mIsSystemNotification != null && mIsSystemNotification) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldSuppressVisualEffect(int effect) {
+ if (isExemptFromDndVisualSuppression()) {
+ return false;
+ }
+ return (suppressedVisualEffects & effect) != 0;
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressFullScreenIntent() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressPeek() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressStatusBar() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressAmbient() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ /**
+ * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressNotificationList() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -706,13 +766,6 @@
return mEnvironment;
}
- private ShadeController getShadeController() {
- if (mShadeController == null) {
- mShadeController = Dependency.get(ShadeController.class);
- }
- return mShadeController;
- }
-
private NotificationMediaManager getMediaManager() {
if (mMediaManager == null) {
mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -720,20 +773,6 @@
return mMediaManager;
}
- private ForegroundServiceController getFsc() {
- if (mFsc == null) {
- mFsc = Dependency.get(ForegroundServiceController.class);
- }
- return mFsc;
- }
-
- private NotificationLockscreenUserManager getUserManager() {
- if (mUserManager == null) {
- mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- }
- return mUserManager;
- }
-
/**
* Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
*
@@ -778,7 +817,7 @@
}
public Entry remove(String key, RankingMap ranking) {
- Entry removed = null;
+ Entry removed;
synchronized (mEntries) {
removed = mEntries.remove(key);
}
@@ -849,62 +888,12 @@
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public boolean shouldSuppressFullScreenIntent(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
- }
-
- public boolean shouldSuppressPeek(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK);
- }
-
- public boolean shouldSuppressStatusBar(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR);
- }
-
- public boolean shouldSuppressAmbient(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT);
- }
-
- public boolean shouldSuppressNotificationList(Entry entry) {
- return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
- }
-
- private boolean shouldSuppressVisualEffect(Entry entry, int effect) {
- if (isExemptFromDndVisualSuppression(entry)) {
- return false;
- }
- String key = entry.key;
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
- }
- return false;
- }
-
- protected boolean isExemptFromDndVisualSuppression(Entry entry) {
- if (isNotificationBlockedByPolicy(entry.notification.getNotification())) {
- return false;
- }
-
- if ((entry.notification.getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return true;
- }
- if (entry.notification.getNotification().isMediaNotification()) {
- return true;
- }
- if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) {
- return true;
- }
- return false;
- }
-
/**
* Categories that are explicitly called out on DND settings screens are always blocked, if
* DND has flagged them, even if they are foreground or system notifications that might
* otherwise visually bypass DND.
*/
- protected boolean isNotificationBlockedByPolicy(Notification n) {
+ private static boolean isNotificationBlockedByPolicy(Notification n) {
if (isCategory(CATEGORY_CALL, n)
|| isCategory(CATEGORY_MESSAGE, n)
|| isCategory(CATEGORY_ALARM, n)
@@ -915,7 +904,7 @@
return false;
}
- private boolean isCategory(String category, Notification n) {
+ private static boolean isCategory(String category, Notification n) {
return Objects.equals(n.category, category);
}
@@ -1013,7 +1002,7 @@
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
- if (shouldFilterOut(entry)) {
+ if (mNotificationFilter.shouldFilterOut(entry)) {
continue;
}
@@ -1024,87 +1013,6 @@
Collections.sort(mSortedAndFiltered, mRankingComparator);
}
- /**
- * @return true if this notification should NOT be shown right now
- */
- public boolean shouldFilterOut(Entry entry) {
- final StatusBarNotification sbn = entry.notification;
- if (!(getEnvironment().isDeviceProvisioned() ||
- showNotificationEvenIfUnprovisioned(sbn))) {
- return true;
- }
-
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
- return true;
- }
-
- if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
- (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
- || getUserManager().shouldHideNotifications(sbn.getUserId())
- || getUserManager().shouldHideNotifications(sbn.getKey()))) {
- return true;
- }
-
- if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
- return true;
- }
-
- if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
- return true;
- }
-
- if (shouldHide(sbn.getKey())) {
- return true;
- }
-
- if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
- && mGroupManager.isChildInGroupWithSummary(sbn)) {
- return true;
- }
-
- if (getFsc().isDungeonNotification(sbn)
- && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
- // this is a foreground-service disclosure for a user that does not need to show one
- return true;
- }
- if (getFsc().isSystemAlertNotification(sbn)) {
- final String[] apps = sbn.getNotification().extras.getStringArray(
- Notification.EXTRA_FOREGROUND_APPS);
- if (apps != null && apps.length >= 1) {
- if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Q: What kinds of notifications should show during setup?
- // A: Almost none! Only things coming from packages with permission
- // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
- // as relevant for setup (see below).
- public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
- return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
- }
-
- @VisibleForTesting
- static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
- StatusBarNotification sbn) {
- return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
- sbn.getUid()) == PackageManager.PERMISSION_GRANTED
- && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
- }
-
- private static int checkUidPermission(IPackageManager packageManager, String permission,
- int uid) {
- try {
- return packageManager.checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
public void dump(PrintWriter pw, String indent) {
int N = mSortedAndFiltered.size();
pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 535ea62..bcbe860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -17,7 +17,6 @@
import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -26,20 +25,16 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
@@ -64,7 +59,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -97,8 +91,6 @@
BubbleController.BubbleDismissListener {
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean ENABLE_HEADS_UP = true;
- private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
@@ -119,6 +111,8 @@
private final AmbientPulseManager mAmbientPulseManager =
Dependency.get(AmbientPulseManager.class);
private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
// Lazily retrieved dependencies
private NotificationRemoteInputManager mRemoteInputManager;
@@ -138,14 +132,10 @@
private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
protected NotificationData mNotificationData;
- private ContentObserver mHeadsUpObserver;
- protected boolean mUseHeadsUp = false;
- private boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
@VisibleForTesting
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
- private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@Nullable private AlertTransferListener mAlertTransferListener;
private final DeviceProvisionedController.DeviceProvisionedListener
@@ -157,11 +147,6 @@
}
};
- public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
- mDisableNotificationAlerts = disableNotificationAlerts;
- mHeadsUpObserver.onChange(true);
- }
-
public void destroy() {
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
}
@@ -177,8 +162,6 @@
pw.println(entry.notification);
}
}
- pw.print(" mUseHeadsUp=");
- pw.println(mUseHeadsUp);
}
public NotificationEntryManager(Context context) {
@@ -245,36 +228,6 @@
mNotificationData.setHeadsUpManager(mHeadsUpManager);
mListContainer = listContainer;
- mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
- @Override
- public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
- Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
- if (wasUsing != mUseHeadsUp) {
- if (!mUseHeadsUp) {
- Log.d(TAG,
- "dismissing any existing heads up notification on disable event");
- mHeadsUpManager.releaseAllImmediately();
- }
- }
- }
- };
-
- if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
- true,
- mHeadsUpObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
- mHeadsUpObserver);
- }
-
mNotificationLifetimeExtenders.add(mHeadsUpManager);
mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
@@ -285,20 +238,6 @@
}
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
- mHeadsUpObserver.onChange(true); // set up
-
- getRowBinder().setInterruptionStateProvider(new InterruptionStateProvider() {
- @Override
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- return NotificationEntryManager.this.shouldHeadsUp(entry);
- }
-
- @Override
- public boolean shouldPulse(NotificationData.Entry entry) {
- return NotificationEntryManager.this.shouldPulse(entry);
- }
- });
}
public NotificationData getNotificationData() {
@@ -327,7 +266,7 @@
return true;
}
- return mNotificationData.shouldSuppressFullScreenIntent(entry);
+ return entry.shouldSuppressFullScreenIntent();
}
public void performRemoveNotification(StatusBarNotification n) {
@@ -441,7 +380,7 @@
if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
- if (shouldHeadsUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
mHeadsUpManager.showNotification(entry);
// Mark as seen immediately
setNotificationShown(entry.notification);
@@ -450,7 +389,7 @@
}
}
if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
- if (shouldPulse(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
mAmbientPulseManager.showNotification(entry);
} else {
entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
@@ -651,7 +590,7 @@
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
NotificationData.Entry shadeEntry = createNotificationEntry(notification, ranking);
- boolean isHeadsUped = shouldHeadsUp(shadeEntry);
+ boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(shadeEntry)) {
if (DEBUG) {
@@ -767,9 +706,11 @@
boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
if (getShadeController().isDozing()) {
- updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldPulse(entry),
+ alertAgain, mAmbientPulseManager);
} else {
- updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ updateAlertState(entry, mNotificationInterruptionStateProvider.shouldHeadsUp(entry),
+ alertAgain, mHeadsUpManager);
}
updateNotifications();
@@ -828,7 +769,7 @@
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationData.Entry entry : entries) {
- mNotificationRowBinder.onNotificationRankingUpdated(
+ getRowBinder().onNotificationRankingUpdated(
entry,
oldImportances.get(entry.key),
oldAdjustments.get(entry.key),
@@ -851,182 +792,6 @@
}
}
- public void setStatusBarStateListener(
- NotificationViewHierarchyManager.StatusBarStateListener listener) {
- mStatusBarStateListener = listener;
- }
-
- /**
- * Whether the notification should peek in from the top and alert the user.
- *
- * @param entry the entry to check
- * @return true if the entry should heads up, false otherwise
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
- }
- return false;
- }
-
- // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
- // message bubble from the bubble to go through heads up path
- boolean inShade = mStatusBarStateListener != null
- && mStatusBarStateListener.getCurrentState() == SHADE;
- if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: no huns or vr mode");
- }
- return false;
- }
-
- boolean isDreaming = false;
- try {
- isDreaming = mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- }
- boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
-
- if (!inUse) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
- }
- return false;
- }
-
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
- }
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
- }
- return false;
- }
-
- if (!mCallback.canHeadsUp(entry, sbn)) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Whether or not the notification should "pulse" on the user's display when the phone is
- * dozing. This displays the ambient view of the notification.
- *
- * @param entry the entry to check
- * @return true if the entry should ambient pulse, false otherwise
- */
- private boolean shouldPulse(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (!getShadeController().isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
- }
- return false;
- }
-
- if (!canAlertCommon(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
- }
- return false;
- }
-
- if (mNotificationData.getImportance(sbn.getKey())
- < NotificationManager.IMPORTANCE_DEFAULT) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
- }
- return false;
- }
-
- Bundle extras = sbn.getNotification().extras;
- CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
- CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
- if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
- if (DEBUG) {
- Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
- }
- return false;
- }
-
- return true;
- }
-
- /**
- * Common checks between heads up alerting and ambient pulse alerting. See
- * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
- * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
- * fail any of these checks should not alert at all.
- *
- * @param entry the entry to check
- * @return true if these checks pass, false if the notification should not alert
- */
- protected boolean canAlertCommon(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
-
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
- }
- return false;
- }
-
- // Don't alert notifications that are suppressed due to group alert behavior
- if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) {
- Log.d(TAG, "No alerting: suppressed due to group alert behavior");
- }
- return false;
- }
-
- return true;
- }
-
private void setNotificationShown(StatusBarNotification n) {
setNotificationsShown(new String[]{n.getKey()});
}
@@ -1039,10 +804,6 @@
}
}
- private boolean isSnoozedPackage(StatusBarNotification sbn) {
- return mHeadsUpManager.isSnoozed(sbn.getPackageName());
- }
-
/**
* Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
* method.
@@ -1078,22 +839,6 @@
}
/**
- * Interface for retrieving heads-up and pulsing state for an entry.
- */
- public interface InterruptionStateProvider {
- /**
- * Whether the provided entry should be marked as heads-up when inflated.
- */
- boolean shouldHeadsUp(NotificationData.Entry entry);
-
- /**
- * Whether the provided entry should be marked as pulsing (displayed in ambient) when
- * inflated.
- */
- boolean shouldPulse(NotificationData.Entry entry);
- }
-
- /**
* Callback for NotificationEntryManager.
*/
public interface Callback {
@@ -1126,14 +871,5 @@
* @param statusBarNotification notification that is being removed
*/
void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
-
- /**
- * Returns true if NotificationEntryManager can heads up this notification.
- *
- * @param entry entry of the notification that might be heads upped
- * @param sbn notification that might be heads upped
- * @return true if the notification can be heads upped
- */
- boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
new file mode 100644
index 0000000..5e99c38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Component which manages the various reasons a notification might be filtered out. */
+@Singleton
+public class NotificationFilter {
+
+ private final NotificationGroupManager mGroupManager = Dependency.get(
+ NotificationGroupManager.class);
+
+ private NotificationData.KeyguardEnvironment mEnvironment;
+ private ShadeController mShadeController;
+ private ForegroundServiceController mFsc;
+ private NotificationLockscreenUserManager mUserManager;
+
+ @Inject
+ public NotificationFilter() {}
+
+ private NotificationData.KeyguardEnvironment getEnvironment() {
+ if (mEnvironment == null) {
+ mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+ }
+ return mEnvironment;
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ private ForegroundServiceController getFsc() {
+ if (mFsc == null) {
+ mFsc = Dependency.get(ForegroundServiceController.class);
+ }
+ return mFsc;
+ }
+
+ private NotificationLockscreenUserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+ }
+ return mUserManager;
+ }
+
+
+ /**
+ * @return true if the provided notification should NOT be shown right now.
+ */
+ public boolean shouldFilterOut(NotificationData.Entry entry) {
+ final StatusBarNotification sbn = entry.notification;
+ if (!(getEnvironment().isDeviceProvisioned()
+ || showNotificationEvenIfUnprovisioned(sbn))) {
+ return true;
+ }
+
+ if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+ return true;
+ }
+
+ if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+ && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+ || getUserManager().shouldHideNotifications(sbn.getUserId())
+ || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+ return true;
+ }
+
+ if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
+ return true;
+ }
+
+ if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
+ return true;
+ }
+
+ if (entry.suspended) {
+ return true;
+ }
+
+ if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
+ return true;
+ }
+
+ if (getFsc().isDungeonNotification(sbn)
+ && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
+ // this is a foreground-service disclosure for a user that does not need to show one
+ return true;
+ }
+ if (getFsc().isSystemAlertNotification(sbn)) {
+ final String[] apps = sbn.getNotification().extras.getStringArray(
+ Notification.EXTRA_FOREGROUND_APPS);
+ if (apps != null && apps.length >= 1) {
+ if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Q: What kinds of notifications should show during setup?
+ // A: Almost none! Only things coming from packages with permission
+ // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
+ // as relevant for setup (see below).
+ private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+ return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
+ }
+
+ @VisibleForTesting
+ static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
+ StatusBarNotification sbn) {
+ return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
+ sbn.getUid()) == PackageManager.PERMISSION_GRANTED
+ && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+ }
+
+ private static int checkUidPermission(IPackageManager packageManager, String permission,
+ int uid) {
+ try {
+ return packageManager.checkUidPermission(permission, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..8bd0e9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Provides heads-up and pulsing state for notification entries.
+ */
+public class NotificationInterruptionStateProvider {
+
+ private static final String TAG = "InterruptionStateProvider";
+ private static final boolean DEBUG = false;
+ private static final boolean ENABLE_HEADS_UP = true;
+ private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+ private final StatusBarStateController mStatusBarStateController =
+ Dependency.get(StatusBarStateController.class);
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+ private final IDreamManager mDreamManager;
+
+ private NotificationPresenter mPresenter;
+ private ShadeController mShadeController;
+ private HeadsUpManager mHeadsUpManager;
+ private HeadsUpSuppressor mHeadsUpSuppressor;
+
+ private ContentObserver mHeadsUpObserver;
+ @VisibleForTesting
+ protected boolean mUseHeadsUp = false;
+ private boolean mDisableNotificationAlerts;
+
+ public NotificationInterruptionStateProvider(Context context) {
+ this(context,
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+ IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE)));
+ }
+
+ @VisibleForTesting
+ protected NotificationInterruptionStateProvider(
+ Context context,
+ PowerManager powerManager,
+ IDreamManager dreamManager) {
+ mContext = context;
+ mPowerManager = powerManager;
+ mDreamManager = dreamManager;
+ }
+
+ /** Sets up late-binding dependencies for this component. */
+ public void setUpWithPresenter(
+ NotificationPresenter notificationPresenter,
+ HeadsUpManager headsUpManager,
+ HeadsUpSuppressor headsUpSuppressor) {
+ mPresenter = notificationPresenter;
+ mHeadsUpManager = headsUpManager;
+ mHeadsUpSuppressor = headsUpSuppressor;
+
+ mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean wasUsing = mUseHeadsUp;
+ mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
+ && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HEADS_UP_OFF);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (wasUsing != mUseHeadsUp) {
+ if (!mUseHeadsUp) {
+ Log.d(TAG,
+ "dismissing any existing heads up notification on disable event");
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+ }
+ };
+
+ if (ENABLE_HEADS_UP) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+ true,
+ mHeadsUpObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+ mHeadsUpObserver);
+ }
+ mHeadsUpObserver.onChange(true); // set up
+ }
+
+ private ShadeController getShadeController() {
+ if (mShadeController == null) {
+ mShadeController = Dependency.get(ShadeController.class);
+ }
+ return mShadeController;
+ }
+
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
+ // message bubble from the bubble to go through heads up path
+ boolean inShade = mStatusBarStateController.getState() == SHADE;
+ if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
+ return false;
+ }
+
+ boolean isDreaming = false;
+ try {
+ isDreaming = mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query dream manager.", e);
+ }
+ boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressPeek()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ public boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!getShadeController().isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.shouldSuppressAmbient()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ Bundle extras = sbn.getNotification().extras;
+ CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+ CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+ if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link #shouldHeadsUp(NotificationData.Entry)} and
+ * {@link #shouldPulse(NotificationData.Entry)}. Notifications that fail any of these checks
+ * should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationFilter.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
+ if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isSnoozedPackage(StatusBarNotification sbn) {
+ return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+ }
+
+ /** Sets whether to disable all alerts. */
+ public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+ mDisableNotificationAlerts = disableNotificationAlerts;
+ mHeadsUpObserver.onChange(true);
+ }
+
+ protected NotificationPresenter getPresenter() {
+ return mPresenter;
+ }
+
+ /** A component which can suppress heads-up notifications due to the overall state of the UI. */
+ public interface HeadsUpSuppressor {
+ /**
+ * Returns false if the provided notification is ineligible for heads-up according to this
+ * component.
+ *
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return false if the notification can not be heads upped
+ */
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index 824bd81..b241b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -64,6 +64,8 @@
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
private final Context mContext;
private final IStatusBarService mBarService;
@@ -79,7 +81,6 @@
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
- private NotificationEntryManager.InterruptionStateProvider mInterruptionStateProvider;
@Inject
public NotificationRowBinder(Context context) {
@@ -116,11 +117,6 @@
mNotificationClicker = clicker;
}
- public void setInterruptionStateProvider(
- NotificationEntryManager.InterruptionStateProvider interruptionStateProvider) {
- mInterruptionStateProvider = interruptionStateProvider;
- }
-
/**
* Inflates the views for the given entry (possibly asynchronously).
*/
@@ -253,10 +249,10 @@
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setEntry(entry);
- if (mInterruptionStateProvider.shouldHeadsUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
}
- if (mInterruptionStateProvider.shouldPulse(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
}
row.setNeedsRedaction(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 2d5d562..e40835f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -207,7 +207,7 @@
}
// showAmbient == show in shade but not shelf
- if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
+ if (!showAmbient && entry.shouldSuppressStatusBar()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1b43f8f..008c21d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -191,6 +191,7 @@
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -377,6 +378,7 @@
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
+ private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private NotificationRowBinder mNotificationRowBinder;
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected ForegroundServiceController mForegroundServiceController;
@@ -622,6 +624,8 @@
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
@@ -1413,7 +1417,7 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mEntryManager.setDisableNotificationAlerts(
+ mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
(state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c8c9ebe5..e69dea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -91,6 +92,8 @@
Dependency.get(NotificationEntryManager.class);
private final NotificationRowBinder mNotificationRowBinder =
Dependency.get(NotificationRowBinder.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
@@ -173,6 +176,8 @@
mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
mEntryManager, this);
+ mNotificationInterruptionStateProvider.setUpWithPresenter(
+ this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
@@ -225,7 +230,7 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mAmbientPulseManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -282,7 +287,6 @@
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- @Override
public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
if (mShadeController.isDozing()) {
return false;