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
This commit is contained in:
Eliot Courtney
2017-10-20 13:26:58 +09:00
parent 9f5ceae640
commit a6d8cf294d
19 changed files with 1756 additions and 1041 deletions

View File

@@ -23,16 +23,19 @@ import android.util.Log;
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.ScrimController;
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 @@ public class SystemUIFactory {
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 @@ public class SystemUIFactory {
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));
}
}

View File

@@ -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);
}
}

View File

@@ -18,9 +18,21 @@ package com.android.systemui.car;
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 @@ public class CarSystemUIFactory extends SystemUIFactory {
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));
}
}

View File

@@ -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);
}
}

View File

@@ -168,7 +168,8 @@ public class NotificationGutsManager implements Dumpable {
String key = sbn.getKey();
if (key.equals(mKeyToRemoveOnGutsClosed)) {
mKeyToRemoveOnGutsClosed = null;
mPresenter.removeNotification(key, mPresenter.getLatestRankingMap());
mPresenter.getEntryManager().removeNotification(key,
mPresenter.getEntryManager().getLatestRankingMap());
}
});

View File

@@ -59,7 +59,7 @@ public class NotificationListener extends NotificationListenerWithPlugins {
final RankingMap currentRanking = getCurrentRanking();
mPresenter.getHandler().post(() -> {
for (StatusBarNotification sbn : notifications) {
mPresenter.addNotification(sbn, currentRanking);
mPresenter.getEntryManager().addNotification(sbn, currentRanking);
}
});
}
@@ -73,7 +73,8 @@ public class NotificationListener extends NotificationListenerWithPlugins {
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 @@ public class NotificationListener extends NotificationListenerWithPlugins {
// 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 @@ public class NotificationListener extends NotificationListenerWithPlugins {
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 @@ public class NotificationListener extends NotificationListenerWithPlugins {
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);
});
}
}

View File

@@ -81,7 +81,7 @@ public class NotificationLockscreenUserManager implements Dumpable {
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 @@ public class NotificationLockscreenUserManager implements Dumpable {
mUsersAllowingNotifications.clear();
// ... and refresh all the notifications
updateLockscreenNotificationSetting();
mPresenter.updateNotifications();
mPresenter.getEntryManager().updateNotifications();
}
};
@@ -191,7 +191,7 @@ public class NotificationLockscreenUserManager implements Dumpable {
public void onChange(boolean selfChange) {
updateLockscreenNotificationSetting();
if (mDeviceProvisionedController.isDeviceProvisioned()) {
mPresenter.updateNotifications();
mPresenter.getEntryManager().updateNotifications();
}
}
};
@@ -271,13 +271,13 @@ public class NotificationLockscreenUserManager implements Dumpable {
*/
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 @@ public class NotificationLockscreenUserManager implements Dumpable {
}
private boolean packageHasVisibilityOverride(String key) {
return mPresenter.getNotificationData().getVisibilityOverride(key) ==
return mPresenter.getEntryManager().getNotificationData().getVisibilityOverride(key) ==
Notification.VISIBILITY_PRIVATE;
}

View File

@@ -99,7 +99,7 @@ public class NotificationLogger {
// 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++) {

View File

@@ -40,9 +40,10 @@ public class NotificationMediaManager implements Dumpable {
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 class NotificationMediaManager implements Dumpable {
}
};
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 @@ public class NotificationMediaManager implements Dumpable {
// 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 class NotificationMediaManager implements Dumpable {
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 @@ public class NotificationMediaManager implements Dumpable {
}
if (metaDataChanged) {
mPresenter.updateNotifications();
mPresenter.getEntryManager().updateNotifications();
}
mPresenter.updateMediaMetaData(metaDataChanged, true);
}

View File

@@ -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 @@ import java.util.Set;
* 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.
@@ -49,33 +50,16 @@ public interface NotificationPresenter extends NotificationUpdateHandler,
*/
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 @@ public interface NotificationPresenter extends NotificationUpdateHandler,
* @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();
}

View File

@@ -282,7 +282,7 @@ public class NotificationRemoteInputManager implements Dumpable {
@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 @@ public class NotificationRemoteInputManager implements Dumpable {
// 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 @@ public class NotificationRemoteInputManager implements Dumpable {
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();
}

View File

@@ -247,19 +247,6 @@ public class CarStatusBar extends StatusBar implements
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)) {
@@ -387,18 +374,6 @@ public class CarStatusBar extends StatusBar implements
return startActivityWithOptions(intent, options.toBundle());
}
@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

View File

@@ -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()));
}
}

View File

@@ -57,6 +57,7 @@ public class NotificationListenerTest extends SysuiTestCase {
private Set<String> mKeysKeptForRemoteInput;
private NotificationData mNotificationData;
private NotificationRemoteInputManager mRemoteInputManager;
private NotificationEntryManager mEntryManager;
@Before
public void setUp() {
@@ -65,10 +66,12 @@ public class NotificationListenerTest extends SysuiTestCase {
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 class NotificationListenerTest extends SysuiTestCase {
public void testNotificationAddCallsAddNotification() {
mListener.onNotificationPosted(mSbn, mRanking);
waitForIdleSync(mHandler);
verify(mPresenter).addNotification(mSbn, mRanking);
verify(mEntryManager).addNotification(mSbn, mRanking);
}
@Test
@@ -98,14 +101,14 @@ public class NotificationListenerTest extends SysuiTestCase {
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 @@ public class NotificationListenerTest extends SysuiTestCase {
mListener.onNotificationRankingUpdate(mRanking);
waitForIdleSync(mHandler);
// RankingMap may be modified by plugins.
verify(mPresenter).updateNotificationRanking(any());
verify(mEntryManager).updateNotificationRanking(any());
}
}

View File

@@ -55,6 +55,7 @@ import org.junit.runner.RunWith;
@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 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
@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 class NotificationLockscreenUserManagerTest extends SysuiTestCase {
public void testSettingsObserverUpdatesNotifications() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
mLockscreenUserManager.getSettingsObserverForTest().onChange(false);
verify(mPresenter, times(1)).updateNotifications();
verify(mEntryManager, times(1)).updateNotifications();
}
@Test

View File

@@ -55,6 +55,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
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 class NotificationLoggerTest extends SysuiTestCase {
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);

View File

@@ -40,6 +40,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
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 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
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 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
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 {

View File

@@ -37,6 +37,7 @@ import static org.mockito.Mockito.when;
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.nano.MetricsProto.MetricsEvent;
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.CommandQueue;
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 @@ public class StatusBarTest extends SysuiTestCase {
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 class StatusBarTest extends SysuiTestCase {
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;
}
}
}