Merge "Create NotificationListController"
This commit is contained in:
@@ -33,6 +33,13 @@ public interface NotificationEntryListener {
|
||||
default void onPendingEntryAdded(NotificationEntry entry) {
|
||||
}
|
||||
|
||||
// TODO: Combine this with onPreEntryUpdated into "onBeforeEntryFiltered" or similar
|
||||
/**
|
||||
* Called when a new entry is created but before it has been filtered or displayed to the user.
|
||||
*/
|
||||
default void onBeforeNotificationAdded(NotificationEntry entry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new entry is created.
|
||||
*/
|
||||
|
||||
@@ -18,11 +18,9 @@ package com.android.systemui.statusbar.notification;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -41,7 +39,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationInflater;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.util.leak.LeakDetector;
|
||||
|
||||
@@ -68,8 +65,6 @@ public class NotificationEntryManager implements
|
||||
@VisibleForTesting
|
||||
protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
|
||||
|
||||
private final DeviceProvisionedController mDeviceProvisionedController =
|
||||
Dependency.get(DeviceProvisionedController.class);
|
||||
private final ForegroundServiceController mForegroundServiceController =
|
||||
Dependency.get(ForegroundServiceController.class);
|
||||
|
||||
@@ -81,25 +76,12 @@ public class NotificationEntryManager implements
|
||||
private NotificationListenerService.RankingMap mLatestRankingMap;
|
||||
@VisibleForTesting
|
||||
protected NotificationData mNotificationData;
|
||||
private NotificationListContainer mListContainer;
|
||||
|
||||
@VisibleForTesting
|
||||
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
|
||||
= new ArrayList<>();
|
||||
private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
|
||||
|
||||
private final DeviceProvisionedController.DeviceProvisionedListener
|
||||
mDeviceProvisionedListener =
|
||||
new DeviceProvisionedController.DeviceProvisionedListener() {
|
||||
@Override
|
||||
public void onDeviceProvisionedChanged() {
|
||||
updateNotifications();
|
||||
}
|
||||
};
|
||||
|
||||
public void destroy() {
|
||||
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("NotificationEntryManager state:");
|
||||
@@ -151,9 +133,6 @@ public class NotificationEntryManager implements
|
||||
HeadsUpManager headsUpManager) {
|
||||
mPresenter = presenter;
|
||||
mNotificationData.setHeadsUpManager(headsUpManager);
|
||||
mListContainer = listContainer;
|
||||
|
||||
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
|
||||
}
|
||||
|
||||
/** Adds multiple {@link NotificationLifetimeExtender}s. */
|
||||
@@ -227,7 +206,9 @@ public class NotificationEntryManager implements
|
||||
listener.onEntryInflated(entry, inflatedFlags);
|
||||
}
|
||||
mNotificationData.add(entry);
|
||||
tagForeground(entry.notification);
|
||||
for (NotificationEntryListener listener : mNotificationEntryListeners) {
|
||||
listener.onBeforeNotificationAdded(entry);
|
||||
}
|
||||
updateNotifications();
|
||||
for (NotificationEntryListener listener : mNotificationEntryListeners) {
|
||||
listener.onNotificationAdded(entry);
|
||||
@@ -283,7 +264,6 @@ public class NotificationEntryManager implements
|
||||
|
||||
if (entry.rowExists()) {
|
||||
entry.removeRow();
|
||||
mListContainer.cleanUpViewStateForEntry(entry);
|
||||
}
|
||||
|
||||
// Let's remove the children if this was a summary
|
||||
@@ -368,19 +348,6 @@ public class NotificationEntryManager implements
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void tagForeground(StatusBarNotification notification) {
|
||||
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
|
||||
notification.getUserId(), notification.getPackageName());
|
||||
if (activeOps != null) {
|
||||
int N = activeOps.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
|
||||
notification.getPackageName(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotification(StatusBarNotification notification,
|
||||
NotificationListenerService.RankingMap ranking) {
|
||||
@@ -391,15 +358,6 @@ public class NotificationEntryManager implements
|
||||
}
|
||||
}
|
||||
|
||||
public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
|
||||
String foregroundKey = mForegroundServiceController.getStandardLayoutKey(
|
||||
UserHandle.getUserId(uid), pkg);
|
||||
if (foregroundKey != null) {
|
||||
mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
|
||||
updateNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNotificationInternal(StatusBarNotification notification,
|
||||
NotificationListenerService.RankingMap ranking) throws InflationException {
|
||||
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
|
||||
@@ -452,8 +410,9 @@ public class NotificationEntryManager implements
|
||||
|
||||
public void updateNotifications() {
|
||||
mNotificationData.filterAndSort();
|
||||
|
||||
mPresenter.updateNotificationViews();
|
||||
if (mPresenter != null) {
|
||||
mPresenter.updateNotificationViews();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.notification;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.systemui.ForegroundServiceController;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
|
||||
|
||||
/**
|
||||
* Root controller for the list of notifications in the shade.
|
||||
*
|
||||
* TODO: Much of the code in NotificationPresenter should eventually move in here. It will proxy
|
||||
* domain-specific behavior (ARC, etc) to subcontrollers.
|
||||
*/
|
||||
public class NotificationListController {
|
||||
private final NotificationEntryManager mEntryManager;
|
||||
private final NotificationListContainer mListContainer;
|
||||
private final ForegroundServiceController mForegroundServiceController;
|
||||
private final DeviceProvisionedController mDeviceProvisionedController;
|
||||
|
||||
public NotificationListController(
|
||||
NotificationEntryManager entryManager,
|
||||
NotificationListContainer listContainer,
|
||||
ForegroundServiceController foregroundServiceController,
|
||||
DeviceProvisionedController deviceProvisionedController) {
|
||||
mEntryManager = checkNotNull(entryManager);
|
||||
mListContainer = checkNotNull(listContainer);
|
||||
mForegroundServiceController = checkNotNull(foregroundServiceController);
|
||||
mDeviceProvisionedController = checkNotNull(deviceProvisionedController);
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the controller to register listeners on its dependencies. This method must be called
|
||||
* before the controller is ready to perform its duties.
|
||||
*/
|
||||
public void bind() {
|
||||
mEntryManager.addNotificationEntryListener(mEntryListener);
|
||||
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
|
||||
}
|
||||
|
||||
/** Should be called when the list controller is being destroyed. */
|
||||
public void destroy() {
|
||||
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
|
||||
@Override
|
||||
public void onEntryRemoved(
|
||||
NotificationEntry entry,
|
||||
NotificationVisibility visibility,
|
||||
boolean removedByUser) {
|
||||
mListContainer.cleanUpViewStateForEntry(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeforeNotificationAdded(NotificationEntry entry) {
|
||||
tagForeground(entry.notification);
|
||||
}
|
||||
};
|
||||
|
||||
private final DeviceProvisionedListener mDeviceProvisionedListener =
|
||||
new DeviceProvisionedListener() {
|
||||
@Override
|
||||
public void onDeviceProvisionedChanged() {
|
||||
mEntryManager.updateNotifications();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: This method is horrifically inefficient
|
||||
private void tagForeground(StatusBarNotification notification) {
|
||||
ArraySet<Integer> activeOps =
|
||||
mForegroundServiceController.getAppOps(
|
||||
notification.getUserId(), notification.getPackageName());
|
||||
if (activeOps != null) {
|
||||
int len = activeOps.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
|
||||
notification.getPackageName(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** When an app op changes, propagate that change to notifications. */
|
||||
public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
|
||||
String foregroundKey =
|
||||
mForegroundServiceController.getStandardLayoutKey(UserHandle.getUserId(uid), pkg);
|
||||
if (foregroundKey != null) {
|
||||
mEntryManager
|
||||
.getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
|
||||
mEntryManager.updateNotifications();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,6 +196,7 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationClicker;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
|
||||
import com.android.systemui.statusbar.notification.NotificationListController;
|
||||
import com.android.systemui.statusbar.notification.NotificationRowBinder;
|
||||
import com.android.systemui.statusbar.notification.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
@@ -387,6 +388,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
private NotificationGutsManager mGutsManager;
|
||||
protected NotificationLogger mNotificationLogger;
|
||||
protected NotificationEntryManager mEntryManager;
|
||||
private NotificationListController mNotificationListController;
|
||||
private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
|
||||
private NotificationRowBinder mNotificationRowBinder;
|
||||
protected NotificationViewHierarchyManager mViewHierarchyManager;
|
||||
@@ -593,7 +595,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
|
||||
mForegroundServiceController.onAppOpChanged(code, uid, packageName, active);
|
||||
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
|
||||
mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
|
||||
mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1044,6 +1046,13 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager,
|
||||
mNotificationAlertingManager);
|
||||
|
||||
mNotificationListController =
|
||||
new NotificationListController(
|
||||
mEntryManager,
|
||||
(NotificationListContainer) mStackScroller,
|
||||
mForegroundServiceController,
|
||||
mDeviceProvisionedController);
|
||||
|
||||
mAppOpsController.addCallback(APP_OPS, this);
|
||||
mNotificationListener.setUpWithPresenter(mPresenter);
|
||||
mNotificationShelf.setOnActivatedListener(mPresenter);
|
||||
@@ -1056,6 +1065,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
|
||||
|
||||
mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
|
||||
mNotificationListController.bind();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2831,7 +2841,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
} catch (RemoteException e) {
|
||||
// Ignore.
|
||||
}
|
||||
mEntryManager.destroy();
|
||||
mNotificationListController.destroy();
|
||||
// End old BaseStatusBar.destroy().
|
||||
if (mStatusBarWindow != null) {
|
||||
mWindowManager.removeViewImmediate(mStatusBarWindow);
|
||||
|
||||
@@ -22,19 +22,15 @@ 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.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -49,7 +45,6 @@ import android.service.notification.StatusBarNotification;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.ArraySet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
@@ -79,8 +74,6 @@ import com.android.systemui.statusbar.phone.ShadeController;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -346,7 +339,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
|
||||
verify(mEntryListener, never()).onInflationError(any(), any());
|
||||
|
||||
verify(mListContainer).cleanUpViewStateForEntry(mEntry);
|
||||
verify(mPresenter).updateNotificationViews();
|
||||
verify(mEntryListener).onEntryRemoved(
|
||||
mEntry, null, false /* removedByUser */);
|
||||
@@ -400,90 +392,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
mEntry, null, false /* removedByUser */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAppOps_foregroundNoti() {
|
||||
com.android.systemui.util.Assert.isNotMainThread();
|
||||
|
||||
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
|
||||
.thenReturn(mEntry.key);
|
||||
mEntry.setRow(mRow);
|
||||
mEntryManager.getNotificationData().add(mEntry);
|
||||
|
||||
mEntryManager.updateNotificationsForAppOp(
|
||||
AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
|
||||
mEntry.notification.getPackageName(), true);
|
||||
|
||||
verify(mPresenter, times(1)).updateNotificationViews();
|
||||
assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
|
||||
AppOpsManager.OP_CAMERA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAppOps_otherNoti() {
|
||||
com.android.systemui.util.Assert.isNotMainThread();
|
||||
|
||||
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
|
||||
.thenReturn(null);
|
||||
mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
|
||||
|
||||
verify(mPresenter, never()).updateNotificationViews();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNotificationExistingAppOps() {
|
||||
mEntry.setRow(mRow);
|
||||
mEntryManager.getNotificationData().add(mEntry);
|
||||
ArraySet<Integer> expected = new ArraySet<>();
|
||||
expected.add(3);
|
||||
expected.add(235);
|
||||
expected.add(1);
|
||||
|
||||
when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
|
||||
mEntry.notification.getPackageName())).thenReturn(expected);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(
|
||||
mEntry.notification.getUserId(),
|
||||
mEntry.notification.getPackageName())).thenReturn(mEntry.key);
|
||||
|
||||
mEntryManager.tagForeground(mEntry.notification);
|
||||
|
||||
Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size());
|
||||
for (int op : expected) {
|
||||
assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_noExistingAppOps() {
|
||||
mEntry.setRow(mRow);
|
||||
mEntryManager.getNotificationData().add(mEntry);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(
|
||||
mEntry.notification.getUserId(),
|
||||
mEntry.notification.getPackageName())).thenReturn(mEntry.key);
|
||||
when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
|
||||
mEntry.notification.getPackageName())).thenReturn(null);
|
||||
|
||||
mEntryManager.tagForeground(mEntry.notification);
|
||||
Assert.assertEquals(0, mEntry.mActiveAppOps.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_existingAppOpsNotForegroundNoti() {
|
||||
mEntry.setRow(mRow);
|
||||
mEntryManager.getNotificationData().add(mEntry);
|
||||
ArraySet<Integer> ops = new ArraySet<>();
|
||||
ops.add(3);
|
||||
ops.add(235);
|
||||
ops.add(1);
|
||||
when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
|
||||
mEntry.notification.getPackageName())).thenReturn(ops);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(
|
||||
mEntry.notification.getUserId(),
|
||||
mEntry.notification.getPackageName())).thenReturn("something else");
|
||||
|
||||
mEntryManager.tagForeground(mEntry.notification);
|
||||
Assert.assertEquals(0, mEntry.mActiveAppOps.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNotificationRanking() {
|
||||
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.notification;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.Notification;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.systemui.ForegroundServiceController;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationData;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class NotificationListControllerTest extends SysuiTestCase {
|
||||
private NotificationListController mController;
|
||||
|
||||
@Mock private NotificationEntryManager mEntryManager;
|
||||
@Mock private NotificationListContainer mListContainer;
|
||||
@Mock private ForegroundServiceController mForegroundServiceController;
|
||||
@Mock private DeviceProvisionedController mDeviceProvisionedController;
|
||||
|
||||
@Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
|
||||
@Captor private ArgumentCaptor<DeviceProvisionedListener> mProvisionedCaptor;
|
||||
|
||||
private NotificationEntryListener mEntryListener;
|
||||
private DeviceProvisionedListener mProvisionedListener;
|
||||
|
||||
// TODO: Remove this once EntryManager no longer needs to be mocked
|
||||
private NotificationData mNotificationData = new NotificationData();
|
||||
|
||||
private int mNextNotifId = 0;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
|
||||
|
||||
mController = new NotificationListController(
|
||||
mEntryManager,
|
||||
mListContainer,
|
||||
mForegroundServiceController,
|
||||
mDeviceProvisionedController);
|
||||
mController.bind();
|
||||
|
||||
// Capture callbacks passed to mocks
|
||||
verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
|
||||
mEntryListener = mEntryListenerCaptor.getValue();
|
||||
verify(mDeviceProvisionedController).addCallback(mProvisionedCaptor.capture());
|
||||
mProvisionedListener = mProvisionedCaptor.getValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanUpViewStateOnEntryRemoved() {
|
||||
final NotificationEntry entry = buildEntry();
|
||||
mEntryListener.onEntryRemoved(
|
||||
entry,
|
||||
NotificationVisibility.obtain(entry.key, 0, 0, true),
|
||||
false);
|
||||
verify(mListContainer).cleanUpViewStateForEntry(entry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallUpdateNotificationsOnDeviceProvisionedChange() {
|
||||
mProvisionedListener.onDeviceProvisionedChanged();
|
||||
verify(mEntryManager).updateNotifications();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppOps_appOpAddedToForegroundNotif() {
|
||||
// GIVEN a notification associated with a foreground service
|
||||
final NotificationEntry entry = buildEntry();
|
||||
mNotificationData.add(entry);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
|
||||
.thenReturn(entry.key);
|
||||
|
||||
// WHEN we are notified of a new app op
|
||||
mController.updateNotificationsForAppOp(
|
||||
AppOpsManager.OP_CAMERA,
|
||||
entry.notification.getUid(),
|
||||
entry.notification.getPackageName(),
|
||||
true);
|
||||
|
||||
// THEN the app op is added to the entry
|
||||
assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
|
||||
// THEN updateNotifications() is called
|
||||
verify(mEntryManager, times(1)).updateNotifications();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppOps_appOpAddedToUnrelatedNotif() {
|
||||
// GIVEN No current foreground notifs
|
||||
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
|
||||
.thenReturn(null);
|
||||
|
||||
// WHEN An unrelated notification gets a new app op
|
||||
mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
|
||||
|
||||
// THEN We never call updateNotifications()
|
||||
verify(mEntryManager, never()).updateNotifications();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppOps_addNotificationWithExistingAppOps() {
|
||||
// GIVEN a notification with three associated app ops that is associated with a foreground
|
||||
// service
|
||||
final NotificationEntry entry = buildEntry();
|
||||
mNotificationData.add(entry);
|
||||
ArraySet<Integer> expected = new ArraySet<>();
|
||||
expected.add(3);
|
||||
expected.add(235);
|
||||
expected.add(1);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(
|
||||
entry.notification.getUserId(),
|
||||
entry.notification.getPackageName())).thenReturn(entry.key);
|
||||
when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
|
||||
entry.notification.getPackageName())).thenReturn(expected);
|
||||
|
||||
// WHEN the notification is added
|
||||
mEntryListener.onBeforeNotificationAdded(entry);
|
||||
|
||||
// THEN the entry is tagged with all three app ops
|
||||
assertEquals(expected.size(), entry.mActiveAppOps.size());
|
||||
for (int op : expected) {
|
||||
assertTrue("Entry missing op " + op, entry.mActiveAppOps.contains(op));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_addNotificationWithNoExistingAppOps() {
|
||||
// GIVEN a notification with NO associated app ops
|
||||
final NotificationEntry entry = buildEntry();
|
||||
|
||||
mNotificationData.add(entry);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(
|
||||
entry.notification.getUserId(),
|
||||
entry.notification.getPackageName())).thenReturn(entry.key);
|
||||
when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
|
||||
entry.notification.getPackageName())).thenReturn(null);
|
||||
|
||||
// WHEN the notification is added
|
||||
mEntryListener.onBeforeNotificationAdded(entry);
|
||||
|
||||
// THEN the entry doesn't have any app ops associated with it
|
||||
assertEquals(0, entry.mActiveAppOps.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_addNonForegroundNotificationWithExistingAppOps() {
|
||||
// GIVEN a notification with app ops that isn't associated with a foreground service
|
||||
final NotificationEntry entry = buildEntry();
|
||||
mNotificationData.add(entry);
|
||||
ArraySet<Integer> ops = new ArraySet<>();
|
||||
ops.add(3);
|
||||
ops.add(235);
|
||||
ops.add(1);
|
||||
when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
|
||||
entry.notification.getPackageName())).thenReturn(ops);
|
||||
when(mForegroundServiceController.getStandardLayoutKey(
|
||||
entry.notification.getUserId(),
|
||||
entry.notification.getPackageName())).thenReturn("something else");
|
||||
|
||||
// WHEN the notification is added
|
||||
mEntryListener.onBeforeNotificationAdded(entry);
|
||||
|
||||
// THEN the entry doesn't have any app ops associated with it
|
||||
assertEquals(0, entry.mActiveAppOps.size());
|
||||
}
|
||||
|
||||
private NotificationEntry buildEntry() {
|
||||
mNextNotifId++;
|
||||
|
||||
Notification.Builder n = new Notification.Builder(mContext, "")
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text");
|
||||
|
||||
StatusBarNotification notification =
|
||||
new StatusBarNotification(
|
||||
TEST_PACKAGE_NAME,
|
||||
TEST_PACKAGE_NAME,
|
||||
mNextNotifId,
|
||||
null,
|
||||
TEST_UID,
|
||||
0,
|
||||
n.build(),
|
||||
new UserHandle(ActivityManager.getCurrentUser()),
|
||||
null,
|
||||
0);
|
||||
return new NotificationEntry(notification);
|
||||
}
|
||||
|
||||
private static final String TEST_PACKAGE_NAME = "test";
|
||||
private static final int TEST_UID = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user