Split NotificationEntryManager out of StatusBar.
NotificationEntryManager is responsible for the adding, removing, and
updating of notifications among other things, such as their inflation
and their interaction with other Notification*Manager objects.
Bug: 63874929
Bug: 62602530
Test: runtest systemui
Test: Compile and run
Change-Id: I56f8c524875900112cdf9f6120407b61e201172f
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 3177c03..c32e089 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -23,16 +23,19 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -46,6 +49,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.util.function.Consumer;
@@ -119,6 +123,7 @@
providers.put(NotificationLockscreenUserManager.class,
() -> new NotificationLockscreenUserManager(context));
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
+ providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(
Dependency.get(NotificationLockscreenUserManager.class), context));
providers.put(NotificationRemoteInputManager.class,
@@ -129,5 +134,20 @@
providers.put(NotificationLogger.class, () -> new NotificationLogger(
Dependency.get(NotificationListener.class),
Dependency.get(UiOffloadThread.class)));
+ providers.put(NotificationEntryManager.class, () ->
+ new NotificationEntryManager(
+ Dependency.get(NotificationLockscreenUserManager.class),
+ Dependency.get(NotificationGroupManager.class),
+ Dependency.get(NotificationGutsManager.class),
+ Dependency.get(NotificationRemoteInputManager.class),
+ Dependency.get(NotificationMediaManager.class),
+ Dependency.get(ForegroundServiceController.class),
+ Dependency.get(NotificationListener.class),
+ Dependency.get(MetricsLogger.class),
+ Dependency.get(DeviceProvisionedController.class),
+ Dependency.get(UiOffloadThread.class),
+ context));
+ providers.put(NotificationListener.class, () -> new NotificationListener(
+ Dependency.get(NotificationRemoteInputManager.class), context));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
new file mode 100644
index 0000000..37172b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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.car;
+
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationGutsManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+public class CarNotificationEntryManager extends NotificationEntryManager {
+ public CarNotificationEntryManager(
+ NotificationLockscreenUserManager lockscreenUserManager,
+ NotificationGroupManager groupManager,
+ NotificationGutsManager gutsManager,
+ NotificationRemoteInputManager remoteInputManager,
+ NotificationMediaManager mediaManager,
+ ForegroundServiceController foregroundServiceController,
+ NotificationListener notificationListener,
+ MetricsLogger metricsLogger,
+ DeviceProvisionedController deviceProvisionedController,
+ UiOffloadThread uiOffloadThread, Context context) {
+ super(lockscreenUserManager, groupManager, gutsManager, remoteInputManager, mediaManager,
+ foregroundServiceController, notificationListener, metricsLogger,
+ deviceProvisionedController, uiOffloadThread, context);
+ }
+
+ /**
+ * Returns the
+ * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
+ * be triggered when a notification card is long-pressed.
+ */
+ @Override
+ public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+ // For the automative use case, we do not want to the user to be able to interact with
+ // a notification other than a regular click. As a result, just return null for the
+ // long click listener.
+ return null;
+ }
+
+ @Override
+ public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+ // Because space is usually constrained in the auto use-case, there should not be a
+ // pinned notification when the shade has been expanded. Ensure this by not pinning any
+ // notification if the shade is already opened.
+ if (!mPresenter.isPresenterFullyCollapsed()) {
+ return false;
+ }
+
+ return super.shouldPeek(entry, sbn);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index 5a19e7d..24f8b97 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -18,9 +18,21 @@
import android.content.Context;
import android.util.ArrayMap;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Dependency;
import com.android.systemui.Dependency.DependencyProvider;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SystemUIFactory;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationGutsManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.volume.car.CarVolumeDialogController;
/**
@@ -32,5 +44,17 @@
Context context) {
super.injectDependencies(providers, context);
providers.put(VolumeDialogController.class, () -> new CarVolumeDialogController(context));
+ providers.put(NotificationEntryManager.class, () -> new CarNotificationEntryManager(
+ Dependency.get(NotificationLockscreenUserManager.class),
+ Dependency.get(NotificationGroupManager.class),
+ Dependency.get(NotificationGutsManager.class),
+ Dependency.get(NotificationRemoteInputManager.class),
+ Dependency.get(NotificationMediaManager.class),
+ Dependency.get(ForegroundServiceController.class),
+ Dependency.get(NotificationListener.class),
+ Dependency.get(MetricsLogger.class),
+ Dependency.get(DeviceProvisionedController.class),
+ Dependency.get(UiOffloadThread.class),
+ context));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
new file mode 100644
index 0000000..b60d2ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager
+ .FORCE_REMOTE_INPUT_HISTORY;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.os.Build;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.R;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationInflater;
+import com.android.systemui.statusbar.notification.RowInflaterTask;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
+ * It also handles tasks such as their inflation and their interaction with other
+ * Notification.*Manager objects.
+ */
+public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback,
+ ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler {
+ private static final String TAG = "NotificationEntryManager";
+ protected static final boolean DEBUG = false;
+ protected static final boolean ENABLE_HEADS_UP = true;
+ protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ protected final NotificationMessagingUtil mMessagingUtil;
+ protected final Context mContext;
+ protected final NotificationLockscreenUserManager mLockscreenUserManager;
+ protected final NotificationGroupManager mGroupManager;
+ protected final NotificationGutsManager mGutsManager;
+ protected final NotificationRemoteInputManager mRemoteInputManager;
+ protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
+ protected final NotificationMediaManager mMediaManager;
+ protected final MetricsLogger mMetricsLogger;
+ protected final DeviceProvisionedController mDeviceProvisionedController;
+ protected final UiOffloadThread mUiOffloadThread;
+ protected final ForegroundServiceController mForegroundServiceController;
+ protected final NotificationListener mNotificationListener;
+ protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+ protected final ArraySet<NotificationData.Entry> mHeadsUpEntriesToRemoveOnSwitch =
+ new ArraySet<>();
+
+ protected IStatusBarService mBarService;
+ protected NotificationPresenter mPresenter;
+ protected Callback mCallback;
+ protected NotificationStackScrollLayout mStackScroller;
+ protected PowerManager mPowerManager;
+ protected SystemServicesProxy mSystemServicesProxy;
+ protected NotificationListenerService.RankingMap mLatestRankingMap;
+ protected HeadsUpManager mHeadsUpManager;
+ protected NotificationData mNotificationData;
+ protected ContentObserver mHeadsUpObserver;
+ protected boolean mUseHeadsUp = false;
+ protected boolean mDisableNotificationAlerts;
+ protected VisualStabilityManager mVisualStabilityManager;
+
+ private final class NotificationClicker implements View.OnClickListener {
+
+ @Override
+ public void onClick(final View v) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
+ return;
+ }
+
+ mPresenter.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ if (sbn == null) {
+ Log.e(TAG, "NotificationClicker called on an unclickable notification,");
+ return;
+ }
+
+ // Check if the notification is displaying the menu, if so slide notification back
+ if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
+ row.animateTranslateNotification(0);
+ return;
+ }
+
+ // Mark notification for one frame.
+ row.setJustClicked(true);
+ DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
+
+ mCallback.onNotificationClicked(sbn, row);
+ }
+
+ public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+ row.setOnClickListener(this);
+ } else {
+ row.setOnClickListener(null);
+ }
+ }
+ }
+
+ private final DeviceProvisionedController.DeviceProvisionedListener
+ mDeviceProvisionedListener =
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onDeviceProvisionedChanged() {
+ updateNotifications();
+ }
+ };
+
+ public NotificationListenerService.RankingMap getLatestRankingMap() {
+ return mLatestRankingMap;
+ }
+
+ public void setLatestRankingMap(NotificationListenerService.RankingMap latestRankingMap) {
+ mLatestRankingMap = latestRankingMap;
+ }
+
+ public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+ mDisableNotificationAlerts = disableNotificationAlerts;
+ mHeadsUpObserver.onChange(true);
+ }
+
+ public void destroy() {
+ mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
+ }
+
+ public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+ removeNotification(entry.key, getLatestRankingMap());
+ mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+ if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
+ setLatestRankingMap(null);
+ }
+ } else {
+ updateNotificationRanking(null);
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("NotificationEntryManager state:");
+ pw.print(" mPendingNotifications=");
+ if (mPendingNotifications.size() == 0) {
+ pw.println("null");
+ } else {
+ for (NotificationData.Entry entry : mPendingNotifications.values()) {
+ pw.println(entry.notification);
+ }
+ }
+ pw.print(" mUseHeadsUp=");
+ pw.println(mUseHeadsUp);
+ }
+
+ public NotificationEntryManager(NotificationLockscreenUserManager lockscreenUserManager,
+ NotificationGroupManager groupManager,
+ NotificationGutsManager gutsManager,
+ NotificationRemoteInputManager remoteInputManager,
+ NotificationMediaManager mediaManager,
+ ForegroundServiceController foregroundServiceController,
+ NotificationListener notificationListener,
+ MetricsLogger metricsLogger,
+ DeviceProvisionedController deviceProvisionedController,
+ UiOffloadThread uiOffloadThread, Context context) {
+ mLockscreenUserManager = lockscreenUserManager;
+ mGroupManager = groupManager;
+ mGutsManager = gutsManager;
+ mRemoteInputManager = remoteInputManager;
+ mMediaManager = mediaManager;
+ mForegroundServiceController = foregroundServiceController;
+ mNotificationListener = notificationListener;
+ mMetricsLogger = metricsLogger;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mUiOffloadThread = uiOffloadThread;
+ mContext = context;
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mMessagingUtil = new NotificationMessagingUtil(context);
+ mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
+ }
+
+ // TODO: Remove dependency on NotificationStackScrollLayout
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationStackScrollLayout stackScroller,
+ Callback callback,
+ VisualStabilityManager visualStabilityManager,
+ HeadsUpManager headsUpManager) {
+ mPresenter = presenter;
+ mCallback = callback;
+ mStackScroller = stackScroller;
+ mVisualStabilityManager = visualStabilityManager;
+ mNotificationData = new NotificationData(presenter);
+ mHeadsUpManager = headsUpManager;
+ mNotificationData.setHeadsUpManager(mHeadsUpManager);
+
+ mHeadsUpObserver = new ContentObserver(mPresenter.getHandler()) {
+ @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);
+ }
+
+ mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+
+ mHeadsUpObserver.onChange(true); // set up
+ }
+
+ public NotificationData getNotificationData() {
+ return mNotificationData;
+ }
+
+ public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+ return mGutsManager::openGuts;
+ }
+
+ @Override
+ public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
+ mUiOffloadThread.submit(() -> {
+ try {
+ mBarService.onNotificationExpansionChanged(key, userAction, expanded);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ });
+ }
+
+ private boolean shouldSuppressFullScreenIntent(String key) {
+ if (mPresenter.isDeviceInVrMode()) {
+ return true;
+ }
+
+ if (mPowerManager.isInteractive()) {
+ return mNotificationData.shouldSuppressScreenOn(key);
+ } else {
+ return mNotificationData.shouldSuppressScreenOff(key);
+ }
+ }
+
+ private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ entry.notification.getUser().getIdentifier());
+
+ final StatusBarNotification sbn = entry.notification;
+ if (entry.row != null) {
+ entry.reset();
+ updateNotification(entry, pmUser, sbn, entry.row);
+ } else {
+ new RowInflaterTask().inflate(mContext, parent, entry,
+ row -> {
+ bindRow(entry, pmUser, sbn, row);
+ updateNotification(entry, pmUser, sbn, row);
+ });
+ }
+ }
+
+ private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row) {
+ row.setExpansionLogger(this, entry.notification.getKey());
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setOnExpandClickListener(mPresenter);
+ row.setInflationCallback(this);
+ row.setLongPressListener(getNotificationLongClicker());
+ mRemoteInputManager.bindRow(row);
+
+ // Get the app name.
+ // Note that Notification.Builder#bindHeaderAppName has similar logic
+ // but since this field is used in the guts, it must be accurate.
+ // Therefore we will only show the application label, or, failing that, the
+ // package name. No substitutions.
+ final String pkg = sbn.getPackageName();
+ String appname = pkg;
+ try {
+ final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if (info != null) {
+ appname = String.valueOf(pmUser.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
+ }
+ row.setAppName(appname);
+ row.setOnDismissRunnable(() ->
+ performRemoveNotification(row.getStatusBarNotification()));
+ row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ if (ENABLE_REMOTE_INPUT) {
+ row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+ }
+
+ mCallback.onBindRow(entry, pmUser, sbn, row);
+ }
+
+ public void performRemoveNotification(StatusBarNotification n) {
+ NotificationData.Entry entry = mNotificationData.get(n.getKey());
+ mRemoteInputManager.onPerformRemoveNotification(n, entry);
+ final String pkg = n.getPackageName();
+ final String tag = n.getTag();
+ final int id = n.getId();
+ final int userId = n.getUserId();
+ try {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (isHeadsUp(n.getKey())) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStackScroller.hasPulsingNotifications()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
+ mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface);
+ removeNotification(n.getKey(), null);
+
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+
+ mCallback.onPerformRemoveNotification(n);
+ }
+
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
+ void handleNotificationError(StatusBarNotification n, String message) {
+ removeNotification(n.getKey(), null);
+ try {
+ mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+ n.getInitialPid(), message, n.getUserId());
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
+ }
+
+ private void abortExistingInflation(String key) {
+ if (mPendingNotifications.containsKey(key)) {
+ NotificationData.Entry entry = mPendingNotifications.get(key);
+ entry.abortTask();
+ mPendingNotifications.remove(key);
+ }
+ NotificationData.Entry addedEntry = mNotificationData.get(key);
+ if (addedEntry != null) {
+ addedEntry.abortTask();
+ }
+ }
+
+ @Override
+ public void handleInflationException(StatusBarNotification notification, Exception e) {
+ handleNotificationError(notification, e.getMessage());
+ }
+
+ private void addEntry(NotificationData.Entry shadeEntry) {
+ boolean isHeadsUped = shouldPeek(shadeEntry);
+ if (isHeadsUped) {
+ mHeadsUpManager.showNotification(shadeEntry);
+ // Mark as seen immediately
+ setNotificationShown(shadeEntry.notification);
+ }
+ addNotificationViews(shadeEntry);
+ mCallback.onNotificationAdded(shadeEntry);
+ }
+
+ @Override
+ public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ mPendingNotifications.remove(entry.key);
+ // If there was an async task started after the removal, we don't want to add it back to
+ // the list, otherwise we might get leaks.
+ boolean isNew = mNotificationData.get(entry.key) == null;
+ if (isNew && !entry.row.isRemoved()) {
+ addEntry(entry);
+ } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
+ mVisualStabilityManager.onLowPriorityUpdated(entry);
+ mPresenter.updateNotificationViews();
+ }
+ entry.row.setLowPriorityStateUpdated(false);
+ }
+
+ @Override
+ public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
+ boolean deferRemoval = false;
+ abortExistingInflation(key);
+ if (mHeadsUpManager.isHeadsUp(key)) {
+ // A cancel() in response to a remote input shouldn't be delayed, as it makes the
+ // sending look longer than it takes.
+ // Also we should not defer the removal if reordering isn't allowed since otherwise
+ // some notifications can't disappear before the panel is closed.
+ boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
+ && !FORCE_REMOTE_INPUT_HISTORY
+ || !mVisualStabilityManager.isReorderingAllowed();
+ deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
+ }
+ mMediaManager.onNotificationRemoved(key);
+
+ NotificationData.Entry entry = mNotificationData.get(key);
+ if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputManager.getController().isSpinning(key)
+ && entry.row != null && !entry.row.isDismissed()) {
+ StatusBarNotification sbn = entry.notification;
+
+ Notification.Builder b = Notification.Builder
+ .recoverBuilder(mContext, sbn.getNotification().clone());
+ CharSequence[] oldHistory = sbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ CharSequence[] newHistory;
+ if (oldHistory == null) {
+ newHistory = new CharSequence[1];
+ } else {
+ newHistory = new CharSequence[oldHistory.length + 1];
+ System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
+ }
+ newHistory[0] = String.valueOf(entry.remoteInputText);
+ b.setRemoteInputHistory(newHistory);
+
+ Notification newNotification = b.build();
+
+ // Undo any compatibility view inflation
+ newNotification.contentView = sbn.getNotification().contentView;
+ newNotification.bigContentView = sbn.getNotification().bigContentView;
+ newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
+
+ StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+ newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ boolean updated = false;
+ try {
+ updateNotificationInternal(newSbn, null);
+ updated = true;
+ } catch (InflationException e) {
+ deferRemoval = false;
+ }
+ if (updated) {
+ Log.w(TAG, "Keeping notification around after sending remote input "+ entry.key);
+ mRemoteInputManager.getKeysKeptForRemoteInput().add(entry.key);
+ return;
+ }
+ }
+ if (deferRemoval) {
+ mLatestRankingMap = ranking;
+ mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
+ return;
+ }
+
+ if (mRemoteInputManager.onRemoveNotification(entry)) {
+ mLatestRankingMap = ranking;
+ return;
+ }
+
+ if (entry != null && mGutsManager.getExposedGuts() != null
+ && mGutsManager.getExposedGuts() == entry.row.getGuts()
+ && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
+ Log.w(TAG, "Keeping notification because it's showing guts. " + key);
+ mLatestRankingMap = ranking;
+ mGutsManager.setKeyToRemoveOnGutsClosed(key);
+ return;
+ }
+
+ if (entry != null) {
+ mForegroundServiceController.removeNotification(entry.notification);
+ }
+
+ if (entry != null && entry.row != null) {
+ entry.row.setRemoved();
+ mStackScroller.cleanUpViewState(entry.row);
+ }
+ // Let's remove the children if this was a summary
+ handleGroupSummaryRemoved(key);
+ StatusBarNotification old = removeNotificationViews(key, ranking);
+
+ mCallback.onNotificationRemoved(key, old);
+ }
+
+ private StatusBarNotification removeNotificationViews(String key,
+ NotificationListenerService.RankingMap ranking) {
+ NotificationData.Entry entry = mNotificationData.remove(key, ranking);
+ if (entry == null) {
+ Log.w(TAG, "removeNotification for unknown key: " + key);
+ return null;
+ }
+ updateNotifications();
+ Dependency.get(LeakDetector.class).trackGarbage(entry);
+ return entry.notification;
+ }
+
+ /**
+ * Ensures that the group children are cancelled immediately when the group summary is cancelled
+ * instead of waiting for the notification manager to send all cancels. Otherwise this could
+ * lead to flickers.
+ *
+ * This also ensures that the animation looks nice and only consists of a single disappear
+ * animation instead of multiple.
+ * @param key the key of the notification was removed
+ *
+ */
+ private void handleGroupSummaryRemoved(String key) {
+ NotificationData.Entry entry = mNotificationData.get(key);
+ if (entry != null && entry.row != null
+ && entry.row.isSummaryWithChildren()) {
+ if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+ // We don't want to remove children for autobundled notifications as they are not
+ // always cancelled. We only remove them if they were dismissed by the user.
+ return;
+ }
+ List<ExpandableNotificationRow> notificationChildren =
+ entry.row.getNotificationChildren();
+ for (int i = 0; i < notificationChildren.size(); i++) {
+ ExpandableNotificationRow row = notificationChildren.get(i);
+ if ((row.getStatusBarNotification().getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ // the child is a foreground service notification which we can't remove!
+ continue;
+ }
+ row.setKeepInParent(true);
+ // we need to set this state earlier as otherwise we might generate some weird
+ // animations
+ row.setRemoved();
+ }
+ }
+ }
+
+ public void updateNotificationsOnDensityOrFontScaleChanged() {
+ ArrayList<NotificationData.Entry> activeNotifications =
+ mNotificationData.getActiveNotifications();
+ for (int i = 0; i < activeNotifications.size(); i++) {
+ NotificationData.Entry entry = activeNotifications.get(i);
+ boolean exposedGuts = mGutsManager.getExposedGuts() != null
+ && entry.row.getGuts() == mGutsManager.getExposedGuts();
+ entry.row.onDensityOrFontScaleChanged();
+ if (exposedGuts) {
+ mGutsManager.setExposedGuts(entry.row.getGuts());
+ mGutsManager.bindGuts(entry.row);
+ }
+ }
+ }
+
+ private void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row) {
+ row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
+ boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
+ boolean isUpdate = mNotificationData.get(entry.key) != null;
+ boolean wasLowPriority = row.isLowPriority();
+ row.setIsLowPriority(isLowPriority);
+ row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
+ // bind the click event to the content area
+ mNotificationClicker.register(row, sbn);
+
+ // Extract target SDK version.
+ try {
+ ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
+ entry.targetSdk = info.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException ex) {
+ Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
+ }
+ row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
+ && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
+
+ entry.row = row;
+ entry.row.setOnActivatedListener(mPresenter);
+
+ boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
+ mNotificationData.getImportance(sbn.getKey()));
+ boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
+ && !mPresenter.isPresenterFullyCollapsed();
+ row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+ row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ row.updateNotification(entry);
+ }
+
+
+ protected void addNotificationViews(NotificationData.Entry entry) {
+ if (entry == null) {
+ return;
+ }
+ // Add the expanded view and icon.
+ mNotificationData.add(entry);
+ updateNotifications();
+ }
+
+ protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
+ throws InflationException {
+ if (DEBUG) {
+ Log.d(TAG, "createNotificationViews(notification=" + sbn);
+ }
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ Dependency.get(LeakDetector.class).trackInstance(entry);
+ entry.createIcons(mContext, sbn);
+ // Construct the expanded view.
+ inflateViews(entry, mStackScroller);
+ return entry;
+ }
+
+ private void addNotificationInternal(StatusBarNotification notification,
+ NotificationListenerService.RankingMap ranking) throws InflationException {
+ String key = notification.getKey();
+ if (DEBUG) Log.d(TAG, "addNotification key=" + key);
+
+ mNotificationData.updateRanking(ranking);
+ NotificationData.Entry shadeEntry = createNotificationViews(notification);
+ boolean isHeadsUped = shouldPeek(shadeEntry);
+ if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
+ if (shouldSuppressFullScreenIntent(key)) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
+ }
+ } else if (mNotificationData.getImportance(key)
+ < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No Fullscreen intent: not important enough: "
+ + key);
+ }
+ } else {
+ // Stop screensaver if the notification has a fullscreen intent.
+ // (like an incoming phone call)
+ SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
+
+ // not immersive & a fullscreen alert should be shown
+ if (DEBUG)
+ Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+ try {
+ EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+ key);
+ notification.getNotification().fullScreenIntent.send();
+ shadeEntry.notifyFullScreenIntentLaunched();
+ mMetricsLogger.count("note_fullscreen", 1);
+ } catch (PendingIntent.CanceledException e) {
+ }
+ }
+ }
+ abortExistingInflation(key);
+
+ mForegroundServiceController.addNotification(notification,
+ mNotificationData.getImportance(key));
+
+ mPendingNotifications.put(key, shadeEntry);
+ }
+
+ @Override
+ public void addNotification(StatusBarNotification notification,
+ NotificationListenerService.RankingMap ranking) {
+ try {
+ addNotificationInternal(notification, ranking);
+ } catch (InflationException e) {
+ handleInflationException(notification, e);
+ }
+ }
+
+ private boolean alertAgain(NotificationData.Entry oldEntry, Notification newNotification) {
+ return oldEntry == null || !oldEntry.hasInterrupted()
+ || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
+ }
+
+ private void updateNotificationInternal(StatusBarNotification notification,
+ NotificationListenerService.RankingMap ranking) throws InflationException {
+ if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
+
+ final String key = notification.getKey();
+ abortExistingInflation(key);
+ NotificationData.Entry entry = mNotificationData.get(key);
+ if (entry == null) {
+ return;
+ }
+ mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+ mRemoteInputManager.onUpdateNotification(entry);
+
+ if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
+ mGutsManager.setKeyToRemoveOnGutsClosed(null);
+ Log.w(TAG, "Notification that was kept for guts was updated. " + key);
+ }
+
+ Notification n = notification.getNotification();
+ mNotificationData.updateRanking(ranking);
+
+ final StatusBarNotification oldNotification = entry.notification;
+ entry.notification = notification;
+ mGroupManager.onEntryUpdated(entry, oldNotification);
+
+ entry.updateIcons(mContext, notification);
+ inflateViews(entry, mStackScroller);
+
+ mForegroundServiceController.updateNotification(notification,
+ mNotificationData.getImportance(key));
+
+ boolean shouldPeek = shouldPeek(entry, notification);
+ boolean alertAgain = alertAgain(entry, n);
+
+ updateHeadsUp(key, entry, shouldPeek, alertAgain);
+ updateNotifications();
+
+ if (!notification.isClearable()) {
+ // The user may have performed a dismiss action on the notification, since it's
+ // not clearable we should snap it back.
+ mStackScroller.snapViewIfNeeded(entry.row);
+ }
+
+ if (DEBUG) {
+ // Is this for you?
+ boolean isForCurrentUser = mPresenter.isNotificationForCurrentProfiles(notification);
+ Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+ }
+
+ mCallback.onNotificationUpdated(notification);
+ }
+
+ @Override
+ public void updateNotification(StatusBarNotification notification,
+ NotificationListenerService.RankingMap ranking) {
+ try {
+ updateNotificationInternal(notification, ranking);
+ } catch (InflationException e) {
+ handleInflationException(notification, e);
+ }
+ }
+
+ public void updateNotifications() {
+ mNotificationData.filterAndSort();
+
+ mPresenter.updateNotificationViews();
+ }
+
+ public void updateNotificationRanking(NotificationListenerService.RankingMap ranking) {
+ mNotificationData.updateRanking(ranking);
+ updateNotifications();
+ }
+
+ protected boolean shouldPeek(NotificationData.Entry entry) {
+ return shouldPeek(entry, entry.notification);
+ }
+
+ public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
+ return false;
+ }
+
+ if (mNotificationData.shouldFilterOut(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
+ return false;
+ }
+
+ boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
+
+ if (!inUse && !mPresenter.isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mPresenter.isDozing() && mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ return false;
+ }
+
+ if (mPresenter.isDozing() && mNotificationData.shouldSuppressScreenOff(sbn.getKey())) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ return false;
+ }
+
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ return false;
+ }
+
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ return false;
+ }
+
+ // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
+ int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
+ : NotificationManager.IMPORTANCE_HIGH;
+ if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
+ if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ return false;
+ }
+
+ // Don't peek notifications that are suppressed due to group alert behavior
+ if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
+ return false;
+ }
+
+ if (!mCallback.shouldPeek(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected void setNotificationShown(StatusBarNotification n) {
+ setNotificationsShown(new String[]{n.getKey()});
+ }
+
+ protected void setNotificationsShown(String[] keys) {
+ try {
+ mNotificationListener.setNotificationsShown(keys);
+ } catch (RuntimeException e) {
+ Log.d(TAG, "failed setNotificationsShown: ", e);
+ }
+ }
+
+ protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+ return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+ }
+
+ protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
+ boolean alertAgain) {
+ final boolean wasHeadsUp = isHeadsUp(key);
+ if (wasHeadsUp) {
+ if (!shouldPeek) {
+ // We don't want this to be interrupting anymore, lets remove it
+ mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ } else {
+ mHeadsUpManager.updateNotification(entry, alertAgain);
+ }
+ } else if (shouldPeek && alertAgain) {
+ // This notification was updated to be a heads-up, show it!
+ mHeadsUpManager.showNotification(entry);
+ }
+ }
+
+ protected boolean isHeadsUp(String key) {
+ return mHeadsUpManager.isHeadsUp(key);
+ }
+
+ /**
+ * Callback for NotificationEntryManager.
+ */
+ public interface Callback {
+
+ /**
+ * Called when a new entry is created.
+ *
+ * @param shadeEntry entry that was created
+ */
+ void onNotificationAdded(NotificationData.Entry shadeEntry);
+
+ /**
+ * Called when a notification was updated.
+ *
+ * @param notification notification that was updated
+ */
+ void onNotificationUpdated(StatusBarNotification notification);
+
+ /**
+ * Called when a notification was removed.
+ *
+ * @param key key of notification that was removed
+ * @param old StatusBarNotification of the notification before it was removed
+ */
+ void onNotificationRemoved(String key, StatusBarNotification old);
+
+
+ /**
+ * Called when a notification is clicked.
+ *
+ * @param sbn notification that was clicked
+ * @param row row for that notification
+ */
+ void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
+
+ /**
+ * Called when a new notification and row is created.
+ *
+ * @param entry entry for the notification
+ * @param pmUser package manager for user
+ * @param sbn notification
+ * @param row row for the notification
+ */
+ void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row);
+
+ /**
+ * Removes a notification immediately.
+ *
+ * @param statusBarNotification notification that is being removed
+ */
+ void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
+
+ /**
+ * Returns true if NotificationEntryManager should peek this notification.
+ *
+ * @param entry entry of the notification that might be peeked
+ * @param sbn notification that might be peeked
+ * @return true if the notification should be peeked
+ */
+ boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 2e572e1..d6a8af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -168,7 +168,8 @@
String key = sbn.getKey();
if (key.equals(mKeyToRemoveOnGutsClosed)) {
mKeyToRemoveOnGutsClosed = null;
- mPresenter.removeNotification(key, mPresenter.getLatestRankingMap());
+ mPresenter.getEntryManager().removeNotification(key,
+ mPresenter.getEntryManager().getLatestRankingMap());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index a72e8ac..cd51f03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -59,7 +59,7 @@
final RankingMap currentRanking = getCurrentRanking();
mPresenter.getHandler().post(() -> {
for (StatusBarNotification sbn : notifications) {
- mPresenter.addNotification(sbn, currentRanking);
+ mPresenter.getEntryManager().addNotification(sbn, currentRanking);
}
});
}
@@ -73,7 +73,8 @@
processForRemoteInput(sbn.getNotification(), mContext);
String key = sbn.getKey();
mRemoteInputManager.getKeysKeptForRemoteInput().remove(key);
- boolean isUpdate = mPresenter.getNotificationData().get(key) != null;
+ boolean isUpdate =
+ mPresenter.getEntryManager().getNotificationData().get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since` we're not going to show them
// anyway. This is true also when the summary is canceled,
@@ -86,16 +87,17 @@
// Remove existing notification to avoid stale data.
if (isUpdate) {
- mPresenter.removeNotification(key, rankingMap);
+ mPresenter.getEntryManager().removeNotification(key, rankingMap);
} else {
- mPresenter.getNotificationData().updateRanking(rankingMap);
+ mPresenter.getEntryManager().getNotificationData()
+ .updateRanking(rankingMap);
}
return;
}
if (isUpdate) {
- mPresenter.updateNotification(sbn, rankingMap);
+ mPresenter.getEntryManager().updateNotification(sbn, rankingMap);
} else {
- mPresenter.addNotification(sbn, rankingMap);
+ mPresenter.getEntryManager().addNotification(sbn, rankingMap);
}
});
}
@@ -107,7 +109,9 @@
if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
final String key = sbn.getKey();
- mPresenter.getHandler().post(() -> mPresenter.removeNotification(key, rankingMap));
+ mPresenter.getHandler().post(() -> {
+ mPresenter.getEntryManager().removeNotification(key, rankingMap);
+ });
}
}
@@ -116,7 +120,9 @@
if (DEBUG) Log.d(TAG, "onRankingUpdate");
if (rankingMap != null) {
RankingMap r = onPluginRankingUpdate(rankingMap);
- mPresenter.getHandler().post(() -> mPresenter.updateNotificationRanking(r));
+ mPresenter.getHandler().post(() -> {
+ mPresenter.getEntryManager().updateNotificationRanking(r);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 644d834..25eb9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -81,7 +81,7 @@
isCurrentProfile(getSendingUserId())) {
mUsersAllowingPrivateNotifications.clear();
updateLockscreenNotificationSetting();
- mPresenter.updateNotifications();
+ mPresenter.getEntryManager().updateNotifications();
} else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
if (userId != mCurrentUserId && isCurrentProfile(userId)) {
mPresenter.onWorkChallengeChanged();
@@ -182,7 +182,7 @@
mUsersAllowingNotifications.clear();
// ... and refresh all the notifications
updateLockscreenNotificationSetting();
- mPresenter.updateNotifications();
+ mPresenter.getEntryManager().updateNotifications();
}
};
@@ -191,7 +191,7 @@
public void onChange(boolean selfChange) {
updateLockscreenNotificationSetting();
if (mDeviceProvisionedController.isDeviceProvisioned()) {
- mPresenter.updateNotifications();
+ mPresenter.getEntryManager().updateNotifications();
}
}
};
@@ -271,13 +271,13 @@
*/
public boolean shouldHideNotifications(String key) {
return isLockscreenPublicMode(mCurrentUserId)
- && mPresenter.getNotificationData().getVisibilityOverride(key) ==
+ && mPresenter.getEntryManager().getNotificationData().getVisibilityOverride(key) ==
Notification.VISIBILITY_SECRET;
}
public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
return mShowLockscreenNotifications
- && !mPresenter.getNotificationData().isAmbient(sbn.getKey());
+ && !mPresenter.getEntryManager().getNotificationData().isAmbient(sbn.getKey());
}
private void setShowLockscreenNotifications(boolean show) {
@@ -395,7 +395,7 @@
}
private boolean packageHasVisibilityOverride(String key) {
- return mPresenter.getNotificationData().getVisibilityOverride(key) ==
+ return mPresenter.getEntryManager().getNotificationData().getVisibilityOverride(key) ==
Notification.VISIBILITY_PRIVATE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
index e58d801..e958f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
@@ -99,7 +99,7 @@
// notifications.
// 3. Report newly visible and no-longer visible notifications.
// 4. Keep currently visible notifications for next report.
- ArrayList<NotificationData.Entry> activeNotifications = mPresenter.
+ ArrayList<NotificationData.Entry> activeNotifications = mPresenter.getEntryManager().
getNotificationData().getActiveNotifications();
int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 283a6e3..158a986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -40,9 +40,10 @@
private static final String TAG = "NotificationMediaManager";
public static final boolean DEBUG_MEDIA = false;
- private final NotificationPresenter mPresenter;
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
+
+ private NotificationPresenter mPresenter;
private MediaController mMediaController;
private String mMediaNotificationKey;
private MediaMetadata mMediaMetadata;
@@ -73,8 +74,7 @@
}
};
- public NotificationMediaManager(NotificationPresenter presenter, Context context) {
- mPresenter = presenter;
+ public NotificationMediaManager(Context context) {
mContext = context;
mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -82,6 +82,10 @@
// in session state
}
+ public void setUpWithPresenter(NotificationPresenter presenter) {
+ mPresenter = presenter;
+ }
+
public void onNotificationRemoved(String key) {
if (key.equals(mMediaNotificationKey)) {
clearCurrentMediaNotification();
@@ -100,8 +104,8 @@
public void findAndUpdateMediaNotifications() {
boolean metaDataChanged = false;
- synchronized (mPresenter.getNotificationData()) {
- ArrayList<NotificationData.Entry> activeNotifications = mPresenter
+ synchronized (mPresenter.getEntryManager().getNotificationData()) {
+ ArrayList<NotificationData.Entry> activeNotifications = mPresenter.getEntryManager()
.getNotificationData().getActiveNotifications();
final int N = activeNotifications.size();
@@ -188,7 +192,7 @@
}
if (metaDataChanged) {
- mPresenter.updateNotifications();
+ mPresenter.getEntryManager().updateNotifications();
}
mPresenter.updateMediaMetaData(metaDataChanged, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 33c7253..c1dd958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -16,12 +16,11 @@
package com.android.systemui.statusbar;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Handler;
-import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.view.View;
-import java.util.Set;
-
/**
* An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
* for both querying the state of the system (some modularised piece of functionality may
@@ -29,9 +28,11 @@
* for affecting the state of the system (e.g. starting an intent, given that the presenter may
* want to perform some action before doing so).
*/
-public interface NotificationPresenter extends NotificationUpdateHandler,
- NotificationData.Environment, NotificationRemoteInputManager.Callback {
-
+public interface NotificationPresenter extends NotificationData.Environment,
+ NotificationRemoteInputManager.Callback,
+ ExpandableNotificationRow.OnExpandClickListener,
+ ActivatableNotificationView.OnActivatedListener,
+ NotificationEntryManager.Callback {
/**
* Returns true if the presenter is not visible. For example, it may not be necessary to do
* animations if this returns true.
@@ -50,32 +51,15 @@
void startNotificationGutsIntent(Intent intent, int appUid);
/**
- * Returns NotificationData.
- */
- NotificationData getNotificationData();
-
- /**
* Returns the Handler for NotificationPresenter.
*/
Handler getHandler();
- // TODO: Create NotificationEntryManager and move this method to there.
- /**
- * Signals that some notifications have changed, and NotificationPresenter should update itself.
- */
- void updateNotifications();
-
/**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
- // TODO: Create NotificationEntryManager and move this method to there.
- /**
- * Gets the latest ranking map.
- */
- NotificationListenerService.RankingMap getLatestRankingMap();
-
/**
* Called when the locked status of the device is changed for a work profile.
*/
@@ -107,4 +91,27 @@
* @return true iff the device is locked
*/
boolean isDeviceLocked(int userId);
+
+ /**
+ * @return true iff the device is in vr mode
+ */
+ boolean isDeviceInVrMode();
+
+ /**
+ * Returns the NotificationEntryManager for this presenter.
+ *
+ * @return NotificationEntryManager
+ */
+ NotificationEntryManager getEntryManager();
+
+ // TODO: Remove this once the view managing code is pulled out of StatusBar.
+ /**
+ * Updates the visual representation of the notifications.
+ */
+ void updateNotificationViews();
+
+ /**
+ * @return true iff the device is dozing
+ */
+ boolean isDozing();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 7827f62..d0428d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -282,7 +282,7 @@
@Override
public void onRemoteInputSent(NotificationData.Entry entry) {
if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
- mPresenter.removeNotification(entry.key, null);
+ mPresenter.getEntryManager().removeNotification(entry.key, null);
} else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
// We're currently holding onto this notification, but from the apps point of
// view it is already canceled, so we'll need to cancel it on the apps behalf
@@ -290,7 +290,7 @@
// bit.
mPresenter.getHandler().postDelayed(() -> {
if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
- mPresenter.removeNotification(entry.key, null);
+ mPresenter.getEntryManager().removeNotification(entry.key, null);
}
}, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
@@ -336,7 +336,8 @@
for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
NotificationData.Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
mRemoteInputController.removeRemoteInput(entry, null);
- mPresenter.removeNotification(entry.key, mPresenter.getLatestRankingMap());
+ mPresenter.getEntryManager().removeNotification(entry.key,
+ mPresenter.getEntryManager().getLatestRankingMap());
}
mRemoteInputEntriesToRemoveOnCollapse.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5ba6f6a..3ebeb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -247,19 +247,6 @@
return null;
}
- /**
- * Returns the
- * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
- * be triggered when a notification card is long-pressed.
- */
- @Override
- protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- // For the automative use case, we do not want to the user to be able to interact with
- // a notification other than a regular click. As a result, just return null for the
- // long click listener.
- return null;
- }
-
@Override
public void showBatteryView() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -388,18 +375,6 @@
}
@Override
- protected boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by not pinning any
- // notification if the shade is already opened.
- if (mPanelExpanded) {
- return false;
- }
-
- return super.shouldPeek(entry, sbn);
- }
-
- @Override
public void animateExpandNotificationsPanel() {
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by removing all heads-
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 c5349d1..04fe7f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -29,9 +29,6 @@
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager
- .FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -66,14 +63,12 @@
import android.content.IntentSender;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
@@ -88,7 +83,6 @@
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -103,12 +97,9 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -139,7 +130,6 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.MessagingGroup;
import com.android.internal.widget.MessagingMessage;
@@ -149,11 +139,9 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.AutoReinflateContainer;
-import com.android.systemui.DejankUtils;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
-import com.android.systemui.ForegroundServiceController;
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -200,6 +188,7 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationInfo;
import com.android.systemui.statusbar.NotificationListener;
@@ -214,8 +203,6 @@
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.RowInflaterTask;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -239,7 +226,6 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.util.NotificationChannels;
-import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
@@ -255,9 +241,6 @@
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, VisualStabilityManager.Callback, CommandQueue.Callbacks,
- ActivatableNotificationView.OnActivatedListener,
- ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
- ExpandableNotificationRow.OnExpandClickListener, InflationCallback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
public static final boolean MULTIUSER_DEBUG = false;
@@ -270,10 +253,6 @@
protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
- protected static final boolean ENABLE_HEADS_UP = true;
- protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
-
-
// Should match the values in PhoneWindowManager
public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
@@ -309,7 +288,7 @@
// Time after we abort the launch transition.
private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
- private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
+ protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
private static final int STATUS_OR_NAV_TRANSIENT =
View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
@@ -427,6 +406,7 @@
private NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
+ protected NotificationEntryManager mEntryManager;
// for disabling the status bar
private int mDisabled1 = 0;
@@ -478,23 +458,6 @@
};
protected final H mHandler = createHandler();
- final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
- @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();
- }
- }
- }
- };
private int mInteractingWindows;
private boolean mAutohideSuspended;
@@ -588,7 +551,6 @@
}
};
- private NotificationMessagingUtil mMessagingUtil;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private UserSwitcherController mUserSwitcherController;
private NetworkController mNetworkController;
@@ -603,11 +565,9 @@
private final LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
protected NotificationIconAreaController mNotificationIconAreaController;
private boolean mReinflateNotificationsOnUserSwitched;
- private final HashMap<String, Entry> mPendingNotifications = new HashMap<>();
private boolean mClearAllEnabled;
@Nullable private View mAmbientIndicationContainer;
private SysuiColorExtractor mColorExtractor;
- private ForegroundServiceController mForegroundServiceController;
private ScreenLifecycle mScreenLifecycle;
@VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
@@ -619,7 +579,6 @@
};
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
- private RankingMap mLatestRankingMap;
private boolean mNoAnimationOnNextBarModeChange;
private FalsingManager mFalsingManager;
@@ -641,6 +600,8 @@
mGroupManager = Dependency.get(NotificationGroupManager.class);
mNotificationLogger = Dependency.get(NotificationLogger.class);
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+ mNotificationListener = Dependency.get(NotificationListener.class);
+ mGroupManager = Dependency.get(NotificationGroupManager.class);
mNetworkController = Dependency.get(NetworkController.class);
mUserSwitcherController = Dependency.get(UserSwitcherController.class);
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
@@ -649,19 +610,18 @@
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mBatteryController = Dependency.get(BatteryController.class);
mAssistManager = Dependency.get(AssistManager.class);
- mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
mGutsManager = Dependency.get(NotificationGutsManager.class);
+ mMediaManager = Dependency.get(NotificationMediaManager.class);
+ mEntryManager = Dependency.get(NotificationEntryManager.class);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
-
mDisplay = mWindowManager.getDefaultDisplay();
updateDisplaySize();
@@ -679,16 +639,12 @@
mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- mNotificationData = new NotificationData(this);
- mMessagingUtil = new NotificationMessagingUtil(mContext);
-
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -698,8 +654,7 @@
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);
- mMediaManager = new NotificationMediaManager(this, mContext);
- mLockscreenUserManager.setUpWithPresenter(this);
+ mMediaManager.setUpWithPresenter(this);
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
@@ -725,6 +680,7 @@
mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
mWallpaperChangedReceiver.onReceive(mContext, null);
+ mLockscreenUserManager.setUpWithPresenter(this);
mCommandQueue.disable(switches[0], switches[6], false /* animate */);
setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
fullscreenStackBounds, dockedStackBounds);
@@ -739,7 +695,6 @@
}
// Set up the initial notification state.
- mNotificationListener = Dependency.get(NotificationListener.class);
mNotificationListener.setUpWithPresenter(this);
if (DEBUG) {
@@ -774,15 +729,6 @@
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
- mHeadsUpObserver.onChange(true); // set up
- 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);
- }
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mUnlockMethodCache.addListener(this);
startKeyguard();
@@ -864,11 +810,13 @@
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
- mNotificationData.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(this, mStackScroller, this, mVisualStabilityManager,
+ mHeadsUpManager);
+
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
@@ -884,7 +832,7 @@
// no window manager? good luck with that
}
- mStackScroller.setLongPressListener(getNotificationLongClicker());
+ mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
mStackScroller.setHeadsUpManager(mHeadsUpManager);
@@ -1107,7 +1055,7 @@
MessagingGroup.dropCache();
// start old BaseStatusBar.onDensityOrFontScaleChanged().
if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
- updateNotificationsOnDensityOrFontScaleChanged();
+ mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
} else {
mReinflateNotificationsOnUserSwitched = true;
}
@@ -1171,20 +1119,6 @@
}
}
- private void updateNotificationsOnDensityOrFontScaleChanged() {
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- for (int i = 0; i < activeNotifications.size(); i++) {
- Entry entry = activeNotifications.get(i);
- boolean exposedGuts = mGutsManager.getExposedGuts() != null
- && entry.row.getGuts() == mGutsManager.getExposedGuts();
- entry.row.onDensityOrFontScaleChanged();
- if (exposedGuts) {
- mGutsManager.setExposedGuts(entry.row.getGuts());
- mGutsManager.bindGuts(entry.row);
- }
- }
- }
-
private void inflateSignalClusters() {
if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar);
}
@@ -1298,7 +1232,7 @@
mStackScroller.setDismissAllInProgress(false);
for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
if (mStackScroller.canChildBeDismissed(rowToRemove)) {
- removeNotification(rowToRemove.getEntry().key, null);
+ mEntryManager.removeNotification(rowToRemove.getEntry().key, null);
} else {
rowToRemove.resetTranslation();
}
@@ -1406,303 +1340,29 @@
return true;
}
- void awakenDreams() {
- SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
- }
-
@Override
- public void addNotification(StatusBarNotification notification, RankingMap ranking) {
- String key = notification.getKey();
- if (DEBUG) Log.d(TAG, "addNotification key=" + key);
-
- mNotificationData.updateRanking(ranking);
- Entry shadeEntry = null;
- try {
- shadeEntry = createNotificationViews(notification);
- } catch (InflationException e) {
- handleInflationException(notification, e);
- return;
- }
- boolean isHeadsUped = shouldPeek(shadeEntry);
- if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (shouldSuppressFullScreenIntent(key)) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
- }
- } else if (mNotificationData.getImportance(key)
- < NotificationManager.IMPORTANCE_HIGH) {
- if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: not important enough: "
- + key);
- }
- } else {
- // Stop screensaver if the notification has a fullscreen intent.
- // (like an incoming phone call)
- awakenDreams();
-
- // not immersive & a fullscreen alert should be shown
- if (DEBUG)
- Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
- try {
- EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
- key);
- notification.getNotification().fullScreenIntent.send();
- shadeEntry.notifyFullScreenIntentLaunched();
- mMetricsLogger.count("note_fullscreen", 1);
- } catch (PendingIntent.CanceledException e) {
- }
- }
- }
- abortExistingInflation(key);
-
- mForegroundServiceController.addNotification(notification,
- mNotificationData.getImportance(key));
-
- mPendingNotifications.put(key, shadeEntry);
- }
-
- private void abortExistingInflation(String key) {
- if (mPendingNotifications.containsKey(key)) {
- Entry entry = mPendingNotifications.get(key);
- entry.abortTask();
- mPendingNotifications.remove(key);
- }
- Entry addedEntry = mNotificationData.get(key);
- if (addedEntry != null) {
- addedEntry.abortTask();
- }
- }
-
- private void addEntry(Entry shadeEntry) {
- boolean isHeadsUped = shouldPeek(shadeEntry);
- if (isHeadsUped) {
- mHeadsUpManager.showNotification(shadeEntry);
- // Mark as seen immediately
- setNotificationShown(shadeEntry.notification);
- }
- addNotificationViews(shadeEntry);
- // Recalculate the position of the sliding windows and the titles.
- setAreThereNotifications();
- }
-
- @Override
- public void handleInflationException(StatusBarNotification notification, Exception e) {
- handleNotificationError(notification, e.getMessage());
- }
-
- @Override
- public void onAsyncInflationFinished(Entry entry) {
- mPendingNotifications.remove(entry.key);
- // If there was an async task started after the removal, we don't want to add it back to
- // the list, otherwise we might get leaks.
- boolean isNew = mNotificationData.get(entry.key) == null;
- if (isNew && !entry.row.isRemoved()) {
- addEntry(entry);
- } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
- mVisualStabilityManager.onLowPriorityUpdated(entry);
- updateNotificationShade();
- }
- entry.row.setLowPriorityStateUpdated(false);
- }
-
- private boolean shouldSuppressFullScreenIntent(String key) {
- if (isDeviceInVrMode()) {
- return true;
- }
-
- if (mPowerManager.isInteractive()) {
- return mNotificationData.shouldSuppressScreenOn(key);
- } else {
- return mNotificationData.shouldSuppressScreenOff(key);
- }
- }
-
- @Override
- public void updateNotificationRanking(RankingMap ranking) {
- mNotificationData.updateRanking(ranking);
- updateNotifications();
- }
-
- @Override
- public void removeNotification(String key, RankingMap ranking) {
- boolean deferRemoval = false;
- abortExistingInflation(key);
- if (mHeadsUpManager.isHeadsUp(key)) {
- // A cancel() in response to a remote input shouldn't be delayed, as it makes the
- // sending look longer than it takes.
- // Also we should not defer the removal if reordering isn't allowed since otherwise
- // some notifications can't disappear before the panel is closed.
- boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
- && !FORCE_REMOTE_INPUT_HISTORY
- || !mVisualStabilityManager.isReorderingAllowed();
- deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
- }
- mMediaManager.onNotificationRemoved(key);
-
- Entry entry = mNotificationData.get(key);
- if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputManager.getController().isSpinning(key)
- && entry.row != null && !entry.row.isDismissed()) {
- StatusBarNotification sbn = entry.notification;
-
- Notification.Builder b = Notification.Builder
- .recoverBuilder(mContext, sbn.getNotification().clone());
- CharSequence[] oldHistory = sbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- CharSequence[] newHistory;
- if (oldHistory == null) {
- newHistory = new CharSequence[1];
- } else {
- newHistory = new CharSequence[oldHistory.length + 1];
- System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
- }
- newHistory[0] = String.valueOf(entry.remoteInputText);
- b.setRemoteInputHistory(newHistory);
-
- Notification newNotification = b.build();
-
- // Undo any compatibility view inflation
- newNotification.contentView = sbn.getNotification().contentView;
- newNotification.bigContentView = sbn.getNotification().bigContentView;
- newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
-
- StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
- boolean updated = false;
- try {
- updateNotificationInternal(newSbn, null);
- updated = true;
- } catch (InflationException e) {
- deferRemoval = false;
- }
- if (updated) {
- Log.w(TAG, "Keeping notification around after sending remote input "+ entry.key);
- mRemoteInputManager.getKeysKeptForRemoteInput().add(entry.key);
- return;
- }
- }
- if (deferRemoval) {
- mLatestRankingMap = ranking;
- mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
- return;
- }
-
- if (mRemoteInputManager.onRemoveNotification(entry)) {
- mLatestRankingMap = ranking;
- return;
- }
-
- if (entry != null && mGutsManager.getExposedGuts() != null
- && mGutsManager.getExposedGuts() == entry.row.getGuts()
- && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
- Log.w(TAG, "Keeping notification because it's showing guts. " + key);
- mLatestRankingMap = ranking;
- mGutsManager.setKeyToRemoveOnGutsClosed(key);
- return;
- }
-
- if (entry != null) {
- mForegroundServiceController.removeNotification(entry.notification);
- }
-
- if (entry != null && entry.row != null) {
- entry.row.setRemoved();
- mStackScroller.cleanUpViewState(entry.row);
- }
- // Let's remove the children if this was a summary
- handleGroupSummaryRemoved(key);
- StatusBarNotification old = removeNotificationViews(key, ranking);
- if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
-
- if (old != null) {
- if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
- && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
- if (mState == StatusBarState.SHADE) {
- animateCollapsePanels();
- } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
- goToKeyguard();
- }
- }
- }
- setAreThereNotifications();
- }
-
- /**
- * Ensures that the group children are cancelled immediately when the group summary is cancelled
- * instead of waiting for the notification manager to send all cancels. Otherwise this could
- * lead to flickers.
- *
- * This also ensures that the animation looks nice and only consists of a single disappear
- * animation instead of multiple.
- * @param key the key of the notification was removed
- *
- */
- private void handleGroupSummaryRemoved(String key) {
- Entry entry = mNotificationData.get(key);
- if (entry != null && entry.row != null
- && entry.row.isSummaryWithChildren()) {
- if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
- // We don't want to remove children for autobundled notifications as they are not
- // always cancelled. We only remove them if they were dismissed by the user.
- return;
- }
- List<ExpandableNotificationRow> notificationChildren =
- entry.row.getNotificationChildren();
- for (int i = 0; i < notificationChildren.size(); i++) {
- ExpandableNotificationRow row = notificationChildren.get(i);
- if ((row.getStatusBarNotification().getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- // the child is a foreground service notification which we can't remove!
- continue;
- }
- row.setKeepInParent(true);
- // we need to set this state earlier as otherwise we might generate some weird
- // animations
- row.setRemoved();
- }
- }
- }
-
- protected void performRemoveNotification(StatusBarNotification n) {
- Entry entry = mNotificationData.get(n.getKey());
- mRemoteInputManager.onPerformRemoveNotification(n, entry);
- // start old BaseStatusBar.performRemoveNotification.
- final String pkg = n.getPackageName();
- final String tag = n.getTag();
- final int id = n.getId();
- final int userId = n.getUserId();
- try {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (isHeadsUp(n.getKey())) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mStackScroller.hasPulsingNotifications()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
- mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface);
- removeNotification(n.getKey(), null);
-
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
+ public void onPerformRemoveNotification(StatusBarNotification n) {
if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
}
- // end old BaseStatusBar.performRemoveNotification.
}
- private void updateNotificationShade() {
- if (mStackScroller == null) return;
+ @Override
+ public void updateNotificationViews() {
+ // The function updateRowStates depends on both of these being non-null, so check them here.
+ // We may be called before they are set from DeviceProvisionedController's callback.
+ if (mStackScroller == null || mScrimController == null) return;
// Do not modify the notifications during collapse.
if (isCollapsing()) {
- addPostCollapseAction(this::updateNotificationShade);
+ addPostCollapseAction(this::updateNotificationViews);
return;
}
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
+ .getActiveNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
@@ -1814,7 +1474,36 @@
updateQsExpansionEnabled();
// Let's also update the icons
- mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
+ mNotificationIconAreaController.updateNotificationIcons(
+ mEntryManager.getNotificationData());
+ }
+
+ @Override
+ public void onNotificationAdded(Entry shadeEntry) {
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationUpdated(StatusBarNotification notification) {
+ setAreThereNotifications();
+ }
+
+ @Override
+ public void onNotificationRemoved(String key, StatusBarNotification old) {
+ if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
+
+ if (old != null) {
+ if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
+ && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
+ if (mState == StatusBarState.SHADE) {
+ animateCollapsePanels();
+ } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
+ goToKeyguard();
+ }
+ }
+ }
+ setAreThereNotifications();
}
/**
@@ -1896,7 +1585,8 @@
}
for (ExpandableNotificationRow remove : toRemove) {
parent.removeChildNotification(remove);
- if (mNotificationData.get(remove.getStatusBarNotification().getKey()) == null) {
+ if (mEntryManager.getNotificationData().get(
+ remove.getStatusBarNotification().getKey()) == null) {
// We only want to add an animation if the view is completely removed
// otherwise it's just a transfer
mStackScroller.notifyGroupChildRemoved(remove,
@@ -1949,7 +1639,7 @@
private void updateEmptyShadeView() {
boolean showEmptyShadeView =
mState != StatusBarState.KEYGUARD &&
- mNotificationData.getActiveNotifications().size() == 0;
+ mEntryManager.getNotificationData().getActiveNotifications().size() == 0;
mNotificationPanel.showEmptyShadeView(showEmptyShadeView);
}
@@ -1964,7 +1654,8 @@
}
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
currentIndex++;
- if (!mNotificationData.isAmbient(row.getStatusBarNotification().getKey())) {
+ if (!mEntryManager.getNotificationData().isAmbient(
+ row.getStatusBarNotification().getKey())) {
speedBumpIndex = currentIndex;
}
}
@@ -1976,15 +1667,9 @@
return entry.row.getParent() instanceof NotificationStackScrollLayout;
}
- @Override
- public void updateNotifications() {
- mNotificationData.filterAndSort();
-
- updateNotificationShade();
- }
public void requestNotificationUpdate() {
- updateNotifications();
+ mEntryManager.updateNotifications();
}
protected void setAreThereNotifications() {
@@ -1993,7 +1678,7 @@
final boolean clearable = hasActiveNotifications() &&
hasActiveClearableNotifications();
Log.d(TAG, "setAreThereNotifications: N=" +
- mNotificationData.getActiveNotifications().size() + " any=" +
+ mEntryManager.getNotificationData().getActiveNotifications().size() + " any=" +
hasActiveNotifications() + " clearable=" + clearable);
}
@@ -2278,9 +1963,8 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mDisableNotificationAlerts =
- (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
- mHeadsUpObserver.onChange(true);
+ mEntryManager.setDisableNotificationAlerts(
+ (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
}
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
@@ -2343,10 +2027,40 @@
return getBarState() == StatusBarState.KEYGUARD;
}
+ @Override
public boolean isDozing() {
return mDozing;
}
+ @Override
+ public boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
+ if (mIsOccluded && !isDozing()) {
+ boolean devicePublic = mLockscreenUserManager.
+ isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
+ boolean userPublic = devicePublic
+ || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
+ boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+ if (userPublic && needsRedaction) {
+ return false;
+ }
+ }
+
+ if (sbn.getNotification().fullScreenIntent != null) {
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+ return false;
+ } else if (isDozing()) {
+ // We never want heads up when we are dozing.
+ return false;
+ } else {
+ // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
+ return !mStatusBarKeyguardViewManager.isShowing()
+ || mStatusBarKeyguardViewManager.isOccluded();
+ }
+ }
+ return true;
+ }
+
@Override // NotificationData.Environment
public String getCurrentMediaNotificationKey() {
return mMediaManager.getMediaNotificationKey();
@@ -2415,34 +2129,10 @@
@Override
public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
- if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
- removeNotification(entry.key, mLatestRankingMap);
- mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
- if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
- mLatestRankingMap = null;
- }
- } else {
- updateNotificationRanking(null);
- if (isHeadsUp) {
- mDozeServiceHost.fireNotificationHeadsUp();
- }
- }
+ mEntryManager.onHeadsUpStateChanged(entry, isHeadsUp);
- }
-
- protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
- boolean alertAgain) {
- final boolean wasHeadsUp = isHeadsUp(key);
- if (wasHeadsUp) {
- if (!shouldPeek) {
- // We don't want this to be interrupting anymore, lets remove it
- mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
- } else {
- mHeadsUpManager.updateNotification(entry, alertAgain);
- }
- } else if (shouldPeek && alertAgain) {
- // This notification was updated to be a heads-up, show it!
- mHeadsUpManager.showNotification(entry);
+ if (isHeadsUp) {
+ mDozeServiceHost.fireNotificationHeadsUp();
}
}
@@ -2452,14 +2142,6 @@
}
}
- public boolean isHeadsUp(String key) {
- return mHeadsUpManager.isHeadsUp(key);
- }
-
- protected boolean isSnoozedPackage(StatusBarNotification sbn) {
- return mHeadsUpManager.isSnoozed(sbn.getPackageName());
- }
-
public boolean isKeyguardCurrentlySecure() {
return !mUnlockMethodCache.canSkipBouncer();
}
@@ -2491,7 +2173,7 @@
@Override
public void onReorderingAllowed() {
- updateNotifications();
+ mEntryManager.updateNotifications();
}
public boolean isLaunchTransitionFadingAway() {
@@ -3127,14 +2809,6 @@
+ " scroll " + mStackScroller.getScrollX()
+ "," + mStackScroller.getScrollY());
}
- pw.print(" mPendingNotifications=");
- if (mPendingNotifications.size() == 0) {
- pw.println("null");
- } else {
- for (Entry entry : mPendingNotifications.values()) {
- pw.println(entry.notification);
- }
- }
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
pw.print(" mStatusBarWindowState=");
@@ -3146,8 +2820,7 @@
pw.println(Settings.Global.zenModeToString(Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.ZEN_MODE,
Settings.Global.ZEN_MODE_OFF)));
- pw.print(" mUseHeadsUp=");
- pw.println(mUseHeadsUp);
+
if (mStatusBarView != null) {
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
}
@@ -3189,8 +2862,8 @@
}
if (DUMPTRUCK) {
- synchronized (mNotificationData) {
- mNotificationData.dump(pw, " ");
+ synchronized (mEntryManager.getNotificationData()) {
+ mEntryManager.getNotificationData().dump(pw, " ");
}
if (false) {
@@ -3429,7 +3102,8 @@
};
public void resetUserExpandedStates() {
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
+ .getActiveNotifications();
final int notificationCount = activeNotifications.size();
for (int i = 0; i < notificationCount; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
@@ -3484,12 +3158,12 @@
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
animateCollapsePanels();
updatePublicMode();
- mNotificationData.filterAndSort();
+ mEntryManager.getNotificationData().filterAndSort();
if (mReinflateNotificationsOnUserSwitched) {
- updateNotificationsOnDensityOrFontScaleChanged();
+ mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
mReinflateNotificationsOnUserSwitched = false;
}
- updateNotificationShade();
+ updateNotificationViews();
mMediaManager.clearCurrentMediaNotification();
setLockscreenUser(newUserId);
}
@@ -3499,6 +3173,13 @@
return mLockscreenUserManager;
}
+ @Override
+ public void onBindRow(Entry entry, PackageManager pmUser,
+ StatusBarNotification sbn, ExpandableNotificationRow row) {
+ row.setAboveShelfChangedListener(mAboveShelfObserver);
+ row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
+ }
+
protected void setLockscreenUser(int newUserId) {
mLockscreenWallpaper.setCurrentUser(newUserId);
mScrimController.setCurrentUser(newUserId);
@@ -3559,7 +3240,8 @@
try {
// consider the transition from peek to expanded to be a panel open,
// but not one that clears notification effects.
- int notificationLoad = mNotificationData.getActiveNotifications().size();
+ int notificationLoad = mEntryManager.getNotificationData()
+ .getActiveNotifications().size();
mBarService.onPanelRevealed(false, notificationLoad);
} catch (RemoteException ex) {
// Won't fail unless the world has ended.
@@ -3577,7 +3259,8 @@
!isPresenterFullyCollapsed() &&
(mState == StatusBarState.SHADE
|| mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mNotificationData.getActiveNotifications().size();
+ int notificationLoad = mEntryManager.getNotificationData().getActiveNotifications()
+ .size();
if (pinnedHeadsUp && isPresenterFullyCollapsed()) {
notificationLoad = 1;
}
@@ -3712,7 +3395,7 @@
} catch (RemoteException e) {
// Ignore.
}
- mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
+ mEntryManager.destroy();
// End old BaseStatusBar.destroy().
if (mStatusBarWindow != null) {
mWindowManager.removeViewImmediate(mStatusBarWindow);
@@ -4165,7 +3848,7 @@
updateDozingState();
updatePublicMode();
updateStackScrollerState(goingToFullShade, fromShadeLocked);
- updateNotifications();
+ mEntryManager.updateNotifications();
checkBarModes();
updateScrimController();
updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
@@ -4679,7 +4362,7 @@
@Override
public void onWorkChallengeChanged() {
updatePublicMode();
- updateNotifications();
+ mEntryManager.updateNotifications();
if (mPendingWorkRemoteInputView != null
&& !mLockscreenUserManager.isAnyProfilePublicMode()) {
// Expand notification panel and the notification row, then click on remote input view
@@ -4889,7 +4572,7 @@
}
public boolean hasActiveNotifications() {
- return !mNotificationData.getActiveNotifications().isEmpty();
+ return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
@Override
@@ -5268,7 +4951,6 @@
protected IStatusBarService mBarService;
// all notifications
- protected NotificationData mNotificationData;
protected NotificationStackScrollLayout mStackScroller;
protected NotificationGroupManager mGroupManager;
@@ -5288,14 +4970,10 @@
protected boolean mDeviceInteractive;
protected boolean mVisible;
- protected final ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
- protected boolean mUseHeadsUp = false;
- protected boolean mDisableNotificationAlerts = false;
-
protected DevicePolicyManager mDevicePolicyManager;
protected PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -5304,7 +4982,6 @@
private LockPatternUtils mLockPatternUtils;
private DeviceProvisionedController mDeviceProvisionedController
= Dependency.get(DeviceProvisionedController.class);
- protected SystemServicesProxy mSystemServicesProxy;
// UI-specific methods
@@ -5319,8 +4996,6 @@
protected DismissView mDismissView;
protected EmptyShadeView mEmptyShadeView;
- private final NotificationClicker mNotificationClicker = new NotificationClicker();
-
protected AssistManager mAssistManager;
protected boolean mVrMode;
@@ -5345,13 +5020,10 @@
return mVrMode;
}
- private final DeviceProvisionedListener mDeviceProvisionedListener =
- new DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- updateNotifications();
- }
- };
+ @Override
+ public NotificationEntryManager getEntryManager() {
+ return mEntryManager;
+ }
private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -5377,6 +5049,121 @@
}
};
+ @Override
+ public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
+ Notification notification = sbn.getNotification();
+ final PendingIntent intent = notification.contentIntent != null
+ ? notification.contentIntent
+ : notification.fullScreenIntent;
+ final String notificationKey = sbn.getKey();
+
+ final boolean afterKeyguardGone = intent.isActivity()
+ && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+ mLockscreenUserManager.getCurrentUserId());
+ dismissKeyguardThenExecute(() -> {
+ // TODO: Some of this code may be able to move to NotificationEntryManager.
+ if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
+ // Release the HUN notification to the shade.
+
+ if (isPresenterFullyCollapsed()) {
+ HeadsUpManager.setIsClickedNotification(row, true);
+ }
+ //
+ // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+ // become canceled shortly by NoMan, but we can't assume that.
+ mHeadsUpManager.releaseImmediately(notificationKey);
+ }
+ StatusBarNotification parentToCancel = null;
+ if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+ StatusBarNotification summarySbn =
+ mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+ if (shouldAutoCancel(summarySbn)) {
+ parentToCancel = summarySbn;
+ }
+ }
+ final StatusBarNotification parentToCancelFinal = parentToCancel;
+ final Runnable runnable = () -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ if (intent != null) {
+ // If we are launching a work activity and require to launch
+ // separate work challenge, we defer the activity action and cancel
+ // notification until work challenge is unlocked.
+ if (intent.isActivity()) {
+ final int userId = intent.getCreatorUserHandle().getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ // TODO(b/28935539): should allow certain activities to
+ // bypass work challenge
+ if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(),
+ notificationKey)) {
+ // Show work challenge, do not run PendingIntent and
+ // remove notification
+ return;
+ }
+ }
+ }
+ try {
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending contentIntent failed: " + e);
+
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ }
+ }
+
+ try {
+ mBarService.onNotificationClick(notificationKey);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ if (parentToCancelFinal != null) {
+ // We have to post it to the UI thread for synchronization
+ mHandler.post(() -> {
+ Runnable removeRunnable =
+ () -> mEntryManager.performRemoveNotification(parentToCancelFinal);
+ if (isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade was collapsed
+ addPostCollapseAction(removeRunnable);
+ } else {
+ removeRunnable.run();
+ }
+ });
+ }
+ };
+
+ if (mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isOccluded()) {
+ mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+ } else {
+ new Thread(runnable).start();
+ }
+
+ if (!mNotificationPanel.isFullyCollapsed()) {
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+ true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ } else {
+ return false;
+ }
+ }, afterKeyguardGone);
+ }
+
protected NotificationListener mNotificationListener;
protected void notifyUserAboutHiddenNotifications() {
@@ -5438,14 +5225,6 @@
return mLockscreenUserManager.isCurrentProfile(notificationUserId);
}
- protected void setNotificationShown(StatusBarNotification n) {
- try {
- mNotificationListener.setNotificationsShown(new String[]{n.getKey()});
- } catch (RuntimeException e) {
- Log.d(TAG, "failed setNotificationsShown: ", e);
- }
- }
-
@Override
public NotificationGroupManager getGroupManager() {
return mGroupManager;
@@ -5475,15 +5254,15 @@
}
}
- protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- return (v, x, y, item) -> mGutsManager.openGuts(v, x, y, item);
- }
-
@Override
public void toggleSplitScreen() {
toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
}
+ void awakenDreams() {
+ SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
+ }
+
@Override
public void preloadRecentApps() {
int msg = MSG_PRELOAD_RECENT_APPS;
@@ -5569,96 +5348,6 @@
}
}
- protected void inflateViews(Entry entry, ViewGroup parent) {
- PackageManager pmUser = getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
-
- final StatusBarNotification sbn = entry.notification;
- if (entry.row != null) {
- entry.reset();
- updateNotification(entry, pmUser, sbn, entry.row);
- } else {
- new RowInflaterTask().inflate(mContext, parent, entry,
- row -> {
- bindRow(entry, pmUser, sbn, row);
- updateNotification(entry, pmUser, sbn, row);
- });
- }
-
- }
-
- private void bindRow(Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row) {
- row.setExpansionLogger(this, entry.notification.getKey());
- row.setGroupManager(mGroupManager);
- row.setHeadsUpManager(mHeadsUpManager);
- row.setAboveShelfChangedListener(mAboveShelfObserver);
- row.setOnExpandClickListener(this);
- row.setInflationCallback(this);
- row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
- row.setLongPressListener(getNotificationLongClicker());
- mRemoteInputManager.bindRow(row);
-
- // Get the app name.
- // Note that Notification.Builder#bindHeaderAppName has similar logic
- // but since this field is used in the guts, it must be accurate.
- // Therefore we will only show the application label, or, failing that, the
- // package name. No substitutions.
- final String pkg = sbn.getPackageName();
- String appname = pkg;
- try {
- final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- if (info != null) {
- appname = String.valueOf(pmUser.getApplicationLabel(info));
- }
- } catch (NameNotFoundException e) {
- // Do nothing
- }
- row.setAppName(appname);
- row.setOnDismissRunnable(() ->
- performRemoveNotification(row.getStatusBarNotification()));
- row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- if (ENABLE_REMOTE_INPUT) {
- row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
- }
- }
-
- private void updateNotification(Entry entry, PackageManager pmUser,
- StatusBarNotification sbn, ExpandableNotificationRow row) {
- row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
- boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
- boolean isUpdate = mNotificationData.get(entry.key) != null;
- boolean wasLowPriority = row.isLowPriority();
- row.setIsLowPriority(isLowPriority);
- row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
- // bind the click event to the content area
- mNotificationClicker.register(row, sbn);
-
- // Extract target SDK version.
- try {
- ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
- entry.targetSdk = info.targetSdkVersion;
- } catch (NameNotFoundException ex) {
- Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
- }
- row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
- && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
-
- entry.row = row;
- entry.row.setOnActivatedListener(this);
-
- boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
- mNotificationData.getImportance(sbn.getKey()));
- boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
- row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
- row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
- row.updateNotification(entry);
- }
-
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
if (!isDeviceProvisioned()) return;
@@ -5702,166 +5391,15 @@
}, afterKeyguardGone);
}
-
- private final class NotificationClicker implements View.OnClickListener {
-
- @Override
- public void onClick(final View v) {
- if (!(v instanceof ExpandableNotificationRow)) {
- Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
- return;
- }
-
- wakeUpIfDozing(SystemClock.uptimeMillis(), v);
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final StatusBarNotification sbn = row.getStatusBarNotification();
- if (sbn == null) {
- Log.e(TAG, "NotificationClicker called on an unclickable notification,");
- return;
- }
-
- // Check if the notification is displaying the menu, if so slide notification back
- if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
- row.animateTranslateNotification(0);
- return;
- }
-
- Notification notification = sbn.getNotification();
- final PendingIntent intent = notification.contentIntent != null
- ? notification.contentIntent
- : notification.fullScreenIntent;
- final String notificationKey = sbn.getKey();
-
- // Mark notification for one frame.
- row.setJustClicked(true);
- DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
-
- final boolean afterKeyguardGone = intent.isActivity()
- && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
- mLockscreenUserManager.getCurrentUserId());
- dismissKeyguardThenExecute(() -> {
- if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
- // Release the HUN notification to the shade.
-
- if (isPresenterFullyCollapsed()) {
- HeadsUpManager.setIsClickedNotification(row, true);
- }
- //
- // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
- // become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.releaseImmediately(notificationKey);
- }
- StatusBarNotification parentToCancel = null;
- if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
- StatusBarNotification summarySbn =
- mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
- if (shouldAutoCancel(summarySbn)) {
- parentToCancel = summarySbn;
- }
- }
- final StatusBarNotification parentToCancelFinal = parentToCancel;
- final Runnable runnable = () -> {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- if (intent != null) {
- // If we are launching a work activity and require to launch
- // separate work challenge, we defer the activity action and cancel
- // notification until work challenge is unlocked.
- if (intent.isActivity()) {
- final int userId = intent.getCreatorUserHandle().getIdentifier();
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
- && mKeyguardManager.isDeviceLocked(userId)) {
- // TODO(b/28935539): should allow certain activities to
- // bypass work challenge
- if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(),
- notificationKey)) {
- // Show work challenge, do not run PendingIntent and
- // remove notification
- return;
- }
- }
- }
- try {
- intent.send(null, 0, null, null, null, null, getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending contentIntent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
- }
- }
-
- try {
- mBarService.onNotificationClick(notificationKey);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- if (parentToCancelFinal != null) {
- // We have to post it to the UI thread for synchronization
- mHandler.post(() -> {
- Runnable removeRunnable =
- () -> performRemoveNotification(parentToCancelFinal);
- if (isCollapsing()) {
- // To avoid lags we're only performing the remove
- // after the shade was collapsed
- addPostCollapseAction(removeRunnable);
- } else {
- removeRunnable.run();
- }
- });
- }
- };
-
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
- mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- } else {
- new Thread(runnable).start();
- }
-
- if (!mNotificationPanel.isFullyCollapsed()) {
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
- true /* delayed */);
- visibilityChanged(false);
-
- return true;
- } else {
- return false;
- }
- }, afterKeyguardGone);
+ private boolean shouldAutoCancel(StatusBarNotification sbn) {
+ int flags = sbn.getNotification().flags;
+ if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
+ return false;
}
-
- private boolean shouldAutoCancel(StatusBarNotification sbn) {
- int flags = sbn.getNotification().flags;
- if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
- return false;
- }
- if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return false;
- }
- return true;
+ if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
}
-
- public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
- Notification notification = sbn.getNotification();
- if (notification.contentIntent != null || notification.fullScreenIntent != null) {
- row.setOnClickListener(this);
- } else {
- row.setOnClickListener(null);
- }
- }
+ return true;
}
protected Bundle getActivityOptions() {
@@ -5904,55 +5442,6 @@
}
/**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(StatusBarNotification n, String message) {
- removeNotification(n.getKey(), null);
- try {
- mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
- n.getInitialPid(), message, n.getUserId());
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
- protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
- NotificationData.Entry entry = mNotificationData.remove(key, ranking);
- if (entry == null) {
- Log.w(TAG, "removeNotification for unknown key: " + key);
- return null;
- }
- updateNotifications();
- Dependency.get(LeakDetector.class).trackGarbage(entry);
- return entry.notification;
- }
-
- protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
- throws InflationException {
- if (DEBUG) {
- Log.d(TAG, "createNotificationViews(notification=" + sbn);
- }
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
- Dependency.get(LeakDetector.class).trackInstance(entry);
- entry.createIcons(mContext, sbn);
- // Construct the expanded view.
- inflateViews(entry, mStackScroller);
- return entry;
- }
-
- protected void addNotificationViews(Entry entry) {
- if (entry == null) {
- return;
- }
- // Add the expanded view and icon.
- mNotificationData.add(entry);
- updateNotifications();
- }
-
- /**
* Updates expanded, dimmed and locked states of notification rows.
*/
protected void updateRowStates() {
@@ -6047,163 +5536,10 @@
mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount());
}
- // TODO: Move this to NotificationEntryManager once it is created.
- private void updateNotificationInternal(StatusBarNotification notification,
- RankingMap ranking) throws InflationException {
- if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
-
- final String key = notification.getKey();
- abortExistingInflation(key);
- Entry entry = mNotificationData.get(key);
- if (entry == null) {
- return;
- }
- mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
- mRemoteInputManager.onUpdateNotification(entry);
-
- if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
- mGutsManager.setKeyToRemoveOnGutsClosed(null);
- Log.w(TAG, "Notification that was kept for guts was updated. " + key);
- }
-
- Notification n = notification.getNotification();
- mNotificationData.updateRanking(ranking);
-
- final StatusBarNotification oldNotification = entry.notification;
- entry.notification = notification;
- mGroupManager.onEntryUpdated(entry, oldNotification);
-
- entry.updateIcons(mContext, notification);
- inflateViews(entry, mStackScroller);
-
- mForegroundServiceController.updateNotification(notification,
- mNotificationData.getImportance(key));
-
- boolean shouldPeek = shouldPeek(entry, notification);
- boolean alertAgain = alertAgain(entry, n);
-
- updateHeadsUp(key, entry, shouldPeek, alertAgain);
- updateNotifications();
-
- if (!notification.isClearable()) {
- // The user may have performed a dismiss action on the notification, since it's
- // not clearable we should snap it back.
- mStackScroller.snapViewIfNeeded(entry.row);
- }
-
- if (DEBUG) {
- // Is this for you?
- boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
- Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
- }
-
- setAreThereNotifications();
- }
-
- @Override
- public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
- try {
- updateNotificationInternal(notification, ranking);
- } catch (InflationException e) {
- handleInflationException(notification, e);
- }
- }
-
protected void notifyHeadsUpGoingToSleep() {
maybeEscalateHeadsUp();
}
- private boolean alertAgain(Entry oldEntry, Notification newNotification) {
- return oldEntry == null || !oldEntry.hasInterrupted()
- || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
- }
-
- protected boolean shouldPeek(Entry entry) {
- return shouldPeek(entry, entry.notification);
- }
-
- protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
- if (!mUseHeadsUp || isDeviceInVrMode()) {
- if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
- return false;
- }
-
- if (mNotificationData.shouldFilterOut(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
- return false;
- }
-
- boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
-
- if (!inUse && !isDozing()) {
- if (DEBUG) {
- Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
- }
- return false;
- }
-
- if (!isDozing() && mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- if (isDozing() && mNotificationData.shouldSuppressScreenOff(sbn.getKey())) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
- return false;
- }
-
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
- return false;
- }
-
- // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
- int importanceLevel = isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
- : NotificationManager.IMPORTANCE_HIGH;
- if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
- if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
- return false;
- }
-
- if (mIsOccluded && !isDozing()) {
- boolean devicePublic = mLockscreenUserManager.
- isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
- boolean userPublic = devicePublic
- || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
- boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
- if (userPublic && needsRedaction) {
- return false;
- }
- }
-
- if (sbn.getNotification().fullScreenIntent != null) {
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
- return false;
- } else if (mDozing) {
- // We never want heads up when we are dozing.
- return false;
- } else {
- // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
- return !mStatusBarKeyguardViewManager.isShowing()
- || mStatusBarKeyguardViewManager.isOccluded();
- }
- }
-
- // Don't peek notifications that are suppressed due to group alert behavior
- if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
- return false;
- }
-
- return true;
- }
-
/**
* @return Whether the security bouncer from Keyguard is showing.
*/
@@ -6233,17 +5569,6 @@
return contextForUser.getPackageManager();
}
- @Override
- public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- mUiOffloadThread.submit(() -> {
- try {
- mBarService.onNotificationExpansionChanged(key, userAction, expanded);
- } catch (RemoteException e) {
- // Ignore.
- }
- });
- }
-
public boolean isKeyguardSecure() {
if (mStatusBarKeyguardViewManager == null) {
// startKeyguard() hasn't been called yet, so we don't know.
@@ -6291,20 +5616,10 @@
}
@Override
- public NotificationData getNotificationData() {
- return mNotificationData;
- }
-
- @Override
public Handler getHandler() {
return mHandler;
}
- @Override
- public RankingMap getLatestRankingMap() {
- return mLatestRankingMap;
- }
-
private final NotificationInfo.CheckSaveListener mCheckSaveListener =
(Runnable saveImportance, StatusBarNotification sbn) -> {
// If the user has security enabled, show challenge if the setting is changed.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
new file mode 100644
index 0000000..dcd0c83
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationEntryManagerTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private ExpandableNotificationRow mRow;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotificationGroupManager mGroupManager;
+ @Mock private NotificationGutsManager mGutsManager;
+ @Mock private NotificationRemoteInputManager mRemoteInputManager;
+ @Mock private NotificationMediaManager mMediaManager;
+ @Mock private ForegroundServiceController mForegroundServiceController;
+ @Mock private NotificationListener mNotificationListener;
+ @Mock private MetricsLogger mMetricsLogger;
+ @Mock private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock private NotificationStackScrollLayout mStackScroller;
+ @Mock private NotificationEntryManager.Callback mCallback;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private NotificationListenerService.RankingMap mRankingMap;
+ @Mock private RemoteInputController mRemoteInputController;
+ @Mock private IStatusBarService mBarService;
+
+ private NotificationData.Entry mEntry;
+ private StatusBarNotification mSbn;
+ private Handler mHandler;
+ private TestableNotificationEntryManager mEntryManager;
+ private CountDownLatch mCountDownLatch;
+
+ private class TestableNotificationEntryManager extends NotificationEntryManager {
+ private final CountDownLatch mCountDownLatch;
+
+ public TestableNotificationEntryManager(
+ NotificationLockscreenUserManager lockscreenUserManager,
+ NotificationGroupManager groupManager,
+ NotificationGutsManager gutsManager,
+ NotificationRemoteInputManager remoteInputManager,
+ NotificationMediaManager mediaManager,
+ ForegroundServiceController foregroundServiceController,
+ NotificationListener notificationListener,
+ MetricsLogger metricsLogger,
+ DeviceProvisionedController deviceProvisionedController,
+ UiOffloadThread uiOffloadThread, Context context,
+ IStatusBarService barService) {
+ super(lockscreenUserManager, groupManager, gutsManager, remoteInputManager,
+ mediaManager, foregroundServiceController, notificationListener, metricsLogger,
+ deviceProvisionedController, uiOffloadThread, context);
+ mBarService = barService;
+ mCountDownLatch = new CountDownLatch(1);
+ mUseHeadsUp = true;
+ }
+
+ @Override
+ public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ super.onAsyncInflationFinished(entry);
+
+ mCountDownLatch.countDown();
+ }
+
+ public CountDownLatch getCountDownLatch() {
+ return mCountDownLatch;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(Looper.getMainLooper());
+ mCountDownLatch = new CountDownLatch(1);
+
+ when(mPresenter.getHandler()).thenReturn(mHandler);
+ when(mPresenter.getEntryManager()).thenReturn(mEntryManager);
+ when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(mLockscreenUserManager);
+ when(mPresenter.getGroupManager()).thenReturn(mGroupManager);
+ when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+ // Necessary for layout inflation.
+ when(mStackScroller.generateLayoutParams(any())).thenReturn(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+ 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
+ mEntry = new NotificationData.Entry(mSbn);
+ mEntry.expandedIcon = mock(StatusBarIconView.class);
+
+ mEntryManager = new TestableNotificationEntryManager(mLockscreenUserManager,
+ mGroupManager, mGutsManager, mRemoteInputManager, mMediaManager,
+ mForegroundServiceController, mNotificationListener, mMetricsLogger,
+ mDeviceProvisionedController, mDependency.get(UiOffloadThread.class), mContext,
+ mBarService);
+ mEntryManager.setUpWithPresenter(mPresenter, mStackScroller, mCallback,
+ mVisualStabilityManager, mHeadsUpManager);
+ }
+
+ @Test
+ public void testAddNotification() throws Exception {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ doAnswer(invocation -> {
+ mCountDownLatch.countDown();
+ return null;
+ }).when(mCallback).onBindRow(any(), any(), any(), any());
+
+ // Post on main thread, otherwise we will be stuck waiting here for the inflation finished
+ // callback forever, since it won't execute until the tests ends.
+ mHandler.post(() -> {
+ mEntryManager.addNotification(mSbn, mRankingMap);
+ });
+ assertTrue(mCountDownLatch.await(1, TimeUnit.MINUTES));
+ assertTrue(mEntryManager.getCountDownLatch().await(1, TimeUnit.MINUTES));
+ waitForIdleSync(mHandler);
+
+ // Check that no inflation error occurred.
+ verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
+ any(), anyInt());
+ verify(mForegroundServiceController).addNotification(eq(mSbn), anyInt());
+
+ // Row inflation:
+ ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
+ NotificationData.Entry.class);
+ verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
+ NotificationData.Entry entry = entryCaptor.getValue();
+ verify(mRemoteInputManager).bindRow(entry.row);
+
+ // Row content inflation:
+ verify(mCallback).onNotificationAdded(entry);
+ verify(mPresenter).updateNotificationViews();
+
+ assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
+ assertNotNull(entry.row);
+ }
+
+ @Test
+ public void testUpdateNotification() throws Exception {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ mEntryManager.getNotificationData().add(mEntry);
+
+ mHandler.post(() -> {
+ mEntryManager.updateNotification(mSbn, mRankingMap);
+ });
+ // Wait for content update.
+ mEntryManager.getCountDownLatch().await(1, TimeUnit.MINUTES);
+ waitForIdleSync(mHandler);
+
+ verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
+ any(), anyInt());
+
+ verify(mRemoteInputManager).onUpdateNotification(mEntry);
+ verify(mPresenter).updateNotificationViews();
+ verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
+ verify(mCallback).onNotificationUpdated(mSbn);
+ assertNotNull(mEntry.row);
+ }
+
+ @Test
+ public void testRemoveNotification() throws Exception {
+ com.android.systemui.util.Assert.isNotMainThread();
+
+ mEntry.row = mRow;
+ mEntryManager.getNotificationData().add(mEntry);
+
+ mHandler.post(() -> {
+ mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+ });
+ waitForIdleSync(mHandler);
+
+ verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
+ any(), anyInt());
+
+ verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
+ verify(mRemoteInputManager).onRemoveNotification(mEntry);
+ verify(mForegroundServiceController).removeNotification(mSbn);
+ verify(mStackScroller).cleanUpViewState(mRow);
+ verify(mPresenter).updateNotificationViews();
+ verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
+ verify(mRow).setRemoved();
+
+ assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index ccc3006..b71f510 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -57,6 +57,7 @@
private Set<String> mKeysKeptForRemoteInput;
private NotificationData mNotificationData;
private NotificationRemoteInputManager mRemoteInputManager;
+ private NotificationEntryManager mEntryManager;
@Before
public void setUp() {
@@ -65,10 +66,12 @@
mNotificationData = mock(NotificationData.class);
mRanking = mock(NotificationListenerService.RankingMap.class);
mRemoteInputManager = mock(NotificationRemoteInputManager.class);
+ mEntryManager = mock(NotificationEntryManager.class);
mKeysKeptForRemoteInput = new HashSet<>();
when(mPresenter.getHandler()).thenReturn(mHandler);
- when(mPresenter.getNotificationData()).thenReturn(mNotificationData);
+ when(mPresenter.getEntryManager()).thenReturn(mEntryManager);
+ when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
when(mRemoteInputManager.getKeysKeptForRemoteInput()).thenReturn(mKeysKeptForRemoteInput);
mListener = new NotificationListener(mRemoteInputManager, mContext);
@@ -82,7 +85,7 @@
public void testNotificationAddCallsAddNotification() {
mListener.onNotificationPosted(mSbn, mRanking);
waitForIdleSync(mHandler);
- verify(mPresenter).addNotification(mSbn, mRanking);
+ verify(mEntryManager).addNotification(mSbn, mRanking);
}
@Test
@@ -98,14 +101,14 @@
when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
mListener.onNotificationPosted(mSbn, mRanking);
waitForIdleSync(mHandler);
- verify(mPresenter).updateNotification(mSbn, mRanking);
+ verify(mEntryManager).updateNotification(mSbn, mRanking);
}
@Test
public void testNotificationRemovalCallsRemoveNotification() {
mListener.onNotificationRemoved(mSbn, mRanking);
waitForIdleSync(mHandler);
- verify(mPresenter).removeNotification(mSbn.getKey(), mRanking);
+ verify(mEntryManager).removeNotification(mSbn.getKey(), mRanking);
}
@Test
@@ -113,6 +116,6 @@
mListener.onNotificationRankingUpdate(mRanking);
waitForIdleSync(mHandler);
// RankingMap may be modified by plugins.
- verify(mPresenter).updateNotificationRanking(any());
+ verify(mEntryManager).updateNotificationRanking(any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 4045995..e4368d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -55,6 +55,7 @@
@TestableLooper.RunWithLooper
public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
private NotificationPresenter mPresenter;
+ private NotificationEntryManager mEntryManager;
private TestNotificationLockscreenUserManager mLockscreenUserManager;
private DeviceProvisionedController mDeviceProvisionedController;
private int mCurrentUserId;
@@ -64,27 +65,30 @@
@Before
public void setUp() {
mUserManager = mock(UserManager.class);
- mContext.addMockSystemService(UserManager.class, mUserManager);
- mHandler = new Handler(Looper.getMainLooper());
+ mPresenter = mock(NotificationPresenter.class);
+ mEntryManager = mock(NotificationEntryManager.class);
mDependency.injectMockDependency(DeviceProvisionedController.class);
mDeviceProvisionedController = mDependency.get(DeviceProvisionedController.class);
- mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
- mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
- mLockscreenUserManager);
+
+ mHandler = new Handler(Looper.getMainLooper());
+ mContext.addMockSystemService(UserManager.class, mUserManager);
+ mCurrentUserId = ActivityManager.getCurrentUser();
when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
-
- mPresenter = mock(NotificationPresenter.class);
when(mPresenter.getHandler()).thenReturn(mHandler);
+ when(mPresenter.getEntryManager()).thenReturn(mEntryManager);
+
+ mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
+ mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
+ mLockscreenUserManager);
mLockscreenUserManager.setUpWithPresenter(mPresenter);
- mCurrentUserId = ActivityManager.getCurrentUser();
}
@Test
public void testLockScreenShowNotificationsChangeUpdatesNotifications() {
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- verify(mPresenter, times(1)).updateNotifications();
+ verify(mEntryManager, times(1)).updateNotifications();
}
@Test
@@ -123,7 +127,7 @@
public void testSettingsObserverUpdatesNotifications() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
mLockscreenUserManager.getSettingsObserverForTest().onChange(false);
- verify(mPresenter, times(1)).updateNotifications();
+ verify(mEntryManager, times(1)).updateNotifications();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
index 142ce63..b0396ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
@@ -55,6 +55,7 @@
private static final int TEST_UID = 0;
@Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationListener mListener;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private IStatusBarService mBarService;
@@ -69,7 +70,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mPresenter.getNotificationData()).thenReturn(mNotificationData);
+ when(mPresenter.getEntryManager()).thenReturn(mEntryManager);
+ when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index b881c09..e943fd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -40,6 +40,7 @@
private NotificationData.Entry mEntry;
@Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationEntryManager mEntryManager;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationRemoteInputManager.Callback mCallback;
@@ -53,7 +54,8 @@
mHandler = new Handler(Looper.getMainLooper());
when(mPresenter.getHandler()).thenReturn(mHandler);
- when(mPresenter.getLatestRankingMap()).thenReturn(mRanking);
+ when(mPresenter.getEntryManager()).thenReturn(mEntryManager);
+ when(mEntryManager.getLatestRankingMap()).thenReturn(mRanking);
mRemoteInputManager = new TestableNotificationRemoteInputManager(mLockscreenUserManager,
mContext);
@@ -97,7 +99,7 @@
assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().isEmpty());
verify(mController).removeRemoteInput(mEntry, null);
- verify(mPresenter).removeNotification(mEntry.key, mRanking);
+ verify(mEntryManager).removeNotification(mEntry.key, mRanking);
}
private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 0732866..db83655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -37,6 +37,7 @@
import android.app.Notification;
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
+import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -61,6 +62,7 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.UiOffloadThread;
@@ -72,10 +74,16 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -113,12 +121,19 @@
ArrayList<Entry> mNotificationList;
FingerprintUnlockController mFingerprintUnlockController;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private TestableNotificationEntryManager mEntryManager;
@Before
public void setup() throws Exception {
mContext.setTheme(R.style.Theme_SystemUI_Light);
mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(DeviceProvisionedController.class);
+ mDependency.injectMockDependency(NotificationGroupManager.class);
+ mDependency.injectMockDependency(NotificationGutsManager.class);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
+ mDependency.injectMockDependency(ForegroundServiceController.class);
+ mDependency.injectMockDependency(NotificationListener.class);
mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
CommandQueue commandQueue = mock(CommandQueue.class);
when(commandQueue.asBinder()).thenReturn(new Binder());
@@ -151,11 +166,16 @@
UiOffloadThread.class));
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+
+ mNotificationListener = mDependency.get(NotificationListener.class);
+ mNotificationLogger = mDependency.get(NotificationLogger.class);
+ mEntryManager = new TestableNotificationEntryManager(mMetricsLogger,
+ mSystemServicesProxy, mPowerManager, mContext);
+
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
- mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
- mBarService, mNotificationListener, mNotificationLogger, mScrimController,
- mFingerprintUnlockController);
+ mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
+ mNotificationLogger, mEntryManager, mScrimController, mFingerprintUnlockController);
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
doAnswer(invocation -> {
@@ -170,6 +190,8 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
+ mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar,
+ mock(VisualStabilityManager.class), mHeadsUpManager, mNotificationData);
mNotificationLogger.setUpWithPresenter(mStatusBar, mStackScroller);
when(mStackScroller.getActivatedChild()).thenReturn(null);
@@ -334,7 +356,7 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mStatusBar.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldPeek(entry, sbn));
}
@Test
@@ -355,7 +377,7 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mStatusBar.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldPeek(entry, sbn));
}
@Test
@@ -375,7 +397,7 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mStatusBar.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldPeek(entry, sbn));
}
@Test
@@ -394,7 +416,7 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mStatusBar.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldPeek(entry, sbn));
}
@Test
public void testShouldPeek_suppressedScreenOff_dozing() {
@@ -412,7 +434,7 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mStatusBar.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldPeek(entry, sbn));
}
@Test
@@ -431,7 +453,7 @@
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mStatusBar.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldPeek(entry, sbn));
}
@@ -564,25 +586,24 @@
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
- NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
- PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
+ NotificationStackScrollLayout stack, HeadsUpManager hum,
+ PowerManager pm, NotificationPanelView panelView,
IStatusBarService barService, NotificationListener notificationListener,
- NotificationLogger notificationLogger, ScrimController scrimController,
+ NotificationLogger notificationLogger,
+ TestableNotificationEntryManager entryManager, ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
mStackScroller = stack;
mHeadsUpManager = hum;
- mNotificationData = nd;
- mUseHeadsUp = true;
mPowerManager = pm;
- mSystemServicesProxy = ssp;
mNotificationPanel = panelView;
mBarService = barService;
mNotificationListener = notificationListener;
mNotificationLogger = notificationLogger;
mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
+ mEntryManager = entryManager;
mScrimController = scrimController;
mFingerprintUnlockController = fingerprintUnlockController;
}
@@ -606,5 +627,39 @@
public void setUserSetupForTest(boolean userSetup) {
mUserSetup = userSetup;
}
+
+ }
+
+ private class TestableNotificationEntryManager extends NotificationEntryManager {
+
+ public TestableNotificationEntryManager(MetricsLogger metricsLogger,
+ SystemServicesProxy systemServicesProxy, PowerManager powerManager,
+ Context context) {
+ super(mDependency.get(NotificationLockscreenUserManager.class),
+ mDependency.get(NotificationGroupManager.class),
+ mDependency.get(NotificationGutsManager.class),
+ mDependency.get(NotificationRemoteInputManager.class),
+ mDependency.get(NotificationMediaManager.class),
+ mDependency.get(ForegroundServiceController.class),
+ mDependency.get(NotificationListener.class),
+ metricsLogger,
+ mDependency.get(DeviceProvisionedController.class),
+ mDependency.get(UiOffloadThread.class),
+ context);
+ mSystemServicesProxy = systemServicesProxy;
+ mPowerManager = powerManager;
+ }
+
+ public void setUpForTest(NotificationPresenter presenter,
+ NotificationStackScrollLayout stackScroller,
+ Callback callback,
+ VisualStabilityManager visualStabilityManager,
+ HeadsUpManager headsUpManager,
+ NotificationData notificationData) {
+ super.setUpWithPresenter(presenter, stackScroller, callback, visualStabilityManager,
+ headsUpManager);
+ mNotificationData = notificationData;
+ mUseHeadsUp = true;
+ }
}
}