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:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user