blob: 842f057b96fcd7fb33810d3fbf89e32429dae144 [file] [log] [blame]
/*
* 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.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
import static com.google.common.truth.Truth.assertThat;
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.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
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.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Unit tests for {@link NotificationEntryManager}.
*/
@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 KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationEntryListener mEntryListener;
@Mock private NotifCollectionListener mNotifCollectionListener;
@Mock private NotificationRemoveInterceptor mRemoveInterceptor;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RankingMap mRankingMap;
@Mock private NotificationGroupManagerLegacy mGroupManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private RowInflaterTask mAsyncInflationTask;
@Mock private NotificationEntryManagerLogger mLogger;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private LeakDetector mLeakDetector;
@Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private NotificationRowBinder mNotificationRowBinder;
@Mock private NotificationListener mNotificationListener;
@Mock private IStatusBarService mStatusBarService;
private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock);
private int mId;
private NotificationEntry mEntry;
private DismissedByUserStats mStats;
private StatusBarNotification mSbn;
private NotificationEntryManager mEntryManager;
private void setUserSentiment(String key, int sentiment) {
doAnswer(invocationOnMock -> {
Ranking ranking = (Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
key,
0,
false,
0,
0,
IMPORTANCE_DEFAULT,
null, null,
null, null, null, true, sentiment, false, -1, false, null, null, false, false,
false, null, 0, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
doAnswer(invocationOnMock -> {
Ranking ranking = (Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
key,
0,
false,
0,
0,
IMPORTANCE_DEFAULT,
null, null,
null, null, null, true,
Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
false, smartActions, null, false, false, false, null, 0, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(SmartReplyController.class);
allowTestableLooperAsMainThread();
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(TestableLooper.get(this).getLooper()));
mEntry = createNotification();
mStats = defaultStats(mEntry);
mSbn = mEntry.getSbn();
mEntryManager = new NotificationEntryManager(
mLogger,
mGroupManager,
mNotifPipelineFlags,
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
mStatusBarService,
mock(DumpManager.class),
mBgExecutor
);
mEntryManager.initialize(
mNotificationListener,
new NotificationRankingManager(
() -> mNotificationMediaManager,
mGroupManager,
mHeadsUpManager,
mock(NotificationFilter.class),
mLogger,
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class),
mock(HighPriorityProvider.class),
mEnvironment));
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.addCollectionListener(mNotifCollectionListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
setUserSentiment(mSbn.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
public void testAddNotification_noDuplicateEntriesCreated() {
// GIVEN a notification has been added
mEntryManager.addNotification(mSbn, mRankingMap);
// WHEN the same notification is added multiple times before the previous entry (with
// the same key) didn't finish inflating
mEntryManager.addNotification(mSbn, mRankingMap);
mEntryManager.addNotification(mSbn, mRankingMap);
mEntryManager.addNotification(mSbn, mRankingMap);
// THEN getAllNotifs() only contains exactly one notification with this key
int count = 0;
for (NotificationEntry entry : mEntryManager.getAllNotifs()) {
if (entry.getKey().equals(mSbn.getKey())) {
count++;
}
}
assertEquals("Should only be one entry with key=" + mSbn.getKey() + " in mAllNotifs. "
+ "Instead there are " + count, 1, count);
}
@Test
public void testAddNotification_setsUserSentiment() {
mEntryManager.addNotification(mSbn, mRankingMap);
ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
NotificationEntry.class);
verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
NotificationEntry entry = entryCaptor.getValue();
assertEquals(entry.getUserSentiment(), Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
public void testUpdateNotification_prePostEntryOrder() throws Exception {
TestableLooper.get(this).processAllMessages();
mEntryManager.addActiveNotificationForTest(mEntry);
mEntryManager.updateNotification(mSbn, mRankingMap);
// Ensure that update callbacks happen in correct order
InOrder order = inOrder(mEntryListener, mPresenter, mEntryListener);
order.verify(mEntryListener).onPreEntryUpdated(mEntry);
order.verify(mEntryListener).onPostEntryUpdated(mEntry);
}
@Test
public void testRemoveNotification() {
mEntry.setRow(mRow);
mEntryManager.addActiveNotificationForTest(mEntry);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
verify(mEntryListener).onEntryRemoved(
argThat(matchEntryOnKey()), any(),
eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
verify(mRow).setRemoved();
assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
}
@Test
public void testRemoveUninflatedNotification_removesNotificationFromAllNotifsList() {
// GIVEN an uninflated entry is added
mEntryManager.addNotification(mSbn, mRankingMap);
assertTrue(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
// WHEN the uninflated entry is removed
mEntryManager.performRemoveNotification(mSbn, mock(DismissedByUserStats.class),
UNDEFINED_DISMISS_REASON);
// THEN the entry is still removed from the allNotifications list
assertFalse(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
}
@Test
public void testPerformRemoveNotification_sendRemovalToServer() throws RemoteException {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that doesn't intercept
when(mRemoveInterceptor.onNotificationRemoveRequested(
eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
.thenReturn(false);
// WHEN the notification entry is removed
mEntryManager.performRemoveNotification(mSbn, mStats, REASON_CANCEL);
// THEN notification removal is sent to the server
FakeExecutor.exhaustExecutors(mBgExecutor);
verify(mStatusBarService).onNotificationClear(
mSbn.getPackageName(),
mSbn.getUser().getIdentifier(),
mSbn.getKey(),
mStats.dismissalSurface,
mStats.dismissalSentiment,
mStats.notificationVisibility);
verifyNoMoreInteractions(mStatusBarService);
}
@Test
public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
verify(mEntryListener, never()).onEntryRemoved(argThat(matchEntryOnKey()), any(),
eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
}
/** Regression test for b/201097913. */
@Test
public void testRemoveNotification_whilePending_onlyCollectionListenerNotified() {
// Add and then remove a pending entry (entry that hasn't been inflated).
mEntryManager.addNotification(mSbn, mRankingMap);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// Verify that only the listener for the NEW pipeline is notified.
// Old pipeline:
verify(mEntryListener, never()).onEntryRemoved(
argThat(matchEntryOnKey()), any(), anyBoolean(), anyInt());
// New pipeline:
verify(mNotifCollectionListener).onEntryRemoved(
argThat(matchEntryOnKey()), anyInt());
}
@Test
public void testUpdateNotificationRanking_noChange() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mEntry.setRow(mRow);
mEntryManager.addActiveNotificationForTest(mEntry);
setSmartActions(mEntry.getKey(), null);
mEntryManager.updateNotificationRanking(mRankingMap);
assertThat(mEntry.getSmartActions()).isEmpty();
}
@Test
public void testUpdateNotificationRanking_pendingNotification() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mEntry.setRow(null);
mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
assertEquals(1, mEntry.getSmartActions().size());
assertEquals("action", mEntry.getSmartActions().get(0).title);
}
@Test
public void testUpdatePendingNotification_rankingUpdated() {
// GIVEN a notification with ranking is pending
final Ranking originalRanking = mEntry.getRanking();
mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
// WHEN the same notification has been updated with a new ranking
final int newRank = 2345;
doAnswer(invocationOnMock -> {
Ranking ranking = (Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
mEntry.getKey(),
newRank, /* this changed!! */
false,
0,
0,
IMPORTANCE_DEFAULT,
null, null,
null, null, null, true,
Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
false, null, null, false, false, false, null, 0, false);
return true;
}).when(mRankingMap).getRanking(eq(mEntry.getKey()), any(Ranking.class));
mEntryManager.addNotification(mSbn, mRankingMap);
// THEN ranking for the entry has been updated with new ranking
assertEquals(newRank, mEntry.getRanking().getRank());
}
@Test
public void testNotifyChannelModified_notifiesListeners() {
NotificationChannel channel = mock(NotificationChannel.class);
String pkg = "PKG";
mEntryManager.notifyChannelModified(pkg, UserHandle.CURRENT, channel,
NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
verify(mNotifCollectionListener).onNotificationChannelModified(eq(pkg),
eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
verify(mEntryListener).onNotificationChannelModified(eq(pkg),
eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@Test
public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN a lifetime extender that always tries to extend lifetime
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.addNotificationLifetimeExtender(extender);
// WHEN the notification is removed
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the extender is asked to manage the lifetime
verify(extender).setShouldManageLifetime(mEntry, true);
// THEN the notification is retained
assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, never()).onEntryRemoved(
argThat(matchEntryOnKey()), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
}
@Test
public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
// GIVEN an entry manager with a notification whose life has been extended
mEntryManager.addActiveNotificationForTest(mEntry);
final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
mEntryManager.addNotificationLifetimeExtender(extender);
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
assertTrue(extender.isManaging(mEntry.getKey()));
// WHEN the extender finishes its extension
extender.setExtendLifetimes(false);
extender.getCallback().onSafeToRemove(mEntry.getKey());
// THEN the notification is removed
assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener).onEntryRemoved(
argThat(matchEntryOnKey()), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
}
@Test
public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
// GIVEN an entry manager with a notification whose life has been extended
mEntryManager.addActiveNotificationForTest(mEntry);
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.addNotificationLifetimeExtender(extender);
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// WHEN the notification is updated
mEntryManager.updateNotification(mEntry.getSbn(), mRankingMap);
// THEN the lifetime extension is canceled
verify(extender).setShouldManageLifetime(mEntry, false);
}
@Test
public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN two lifetime extenders, the first which never extends and the second which
// always extends
NotificationLifetimeExtender extender1 = mock(NotificationLifetimeExtender.class);
when(extender1.shouldExtendLifetime(mEntry)).thenReturn(false);
NotificationLifetimeExtender extender2 = mock(NotificationLifetimeExtender.class);
when(extender2.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.addNotificationLifetimeExtender(extender1);
mEntryManager.addNotificationLifetimeExtender(extender2);
// GIVEN a notification was lifetime-extended and extender2 is managing it
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
verify(extender1, never()).setShouldManageLifetime(mEntry, true);
verify(extender2).setShouldManageLifetime(mEntry, true);
// WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN extender2 stops managing the notif and extender1 starts managing it
verify(extender1).setShouldManageLifetime(mEntry, true);
verify(extender2).setShouldManageLifetime(mEntry, false);
}
/**
* Ensure that calling NotificationEntryManager.performRemoveNotification() doesn't crash when
* given a notification that has already been removed from NotificationData.
*/
@Test
public void testPerformRemoveNotification_removedEntry() {
mEntryManager.removeNotification(mSbn.getKey(), null, 0);
mEntryManager.performRemoveNotification(mSbn, mock(DismissedByUserStats.class),
REASON_CANCEL);
}
@Test
public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that intercepts that entry
when(mRemoveInterceptor.onNotificationRemoveRequested(
eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
.thenReturn(true);
// WHEN the notification is removed
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, never()).onEntryRemoved(argThat(matchEntryOnKey()),
any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
}
@Test
public void testRemoveInterceptor_notInterceptedGetsRemoved() {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that doesn't intercept
when(mRemoveInterceptor.onNotificationRemoveRequested(
eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
.thenReturn(false);
// WHEN the notification is removed
mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, atLeastOnce()).onEntryRemoved(argThat(matchEntryOnKey()),
any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
}
/* Tests annexed from NotificationDataTest go here */
@Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
Notification.Builder n = new Notification.Builder(mContext, "di")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
NotificationEntry e2 = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
.setUid(TEST_UID)
.setId(mId++)
.setNotification(n.build())
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
.setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
.build();
mEntryManager.addActiveNotificationForTest(mEntry);
mEntryManager.addActiveNotificationForTest(e2);
when(mEnvironment.isNotificationForCurrentProfiles(mEntry.getSbn())).thenReturn(false);
when(mEnvironment.isNotificationForCurrentProfiles(e2.getSbn())).thenReturn(true);
List<NotificationEntry> result = mEntryManager.getActiveNotificationsForCurrentUser();
assertEquals(result.size(), 1);
junit.framework.Assert.assertEquals(result.get(0), e2);
}
/* End annex */
private boolean entriesContainKey(Collection<NotificationEntry> entries, String key) {
for (NotificationEntry entry : entries) {
if (entry.getSbn().getKey().equals(key)) {
return true;
}
}
return false;
}
private Notification.Action createAction() {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
"action",
PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"),
PendingIntent.FLAG_IMMUTABLE)).build();
}
private ArgumentMatcher<NotificationEntry> matchEntryOnKey() {
return e -> e.getKey().equals(mEntry.getKey());
}
private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender {
private NotificationSafeToRemoveCallback mCallback;
private boolean mExtendLifetimes = true;
private Set<String> mManagedNotifs = new ArraySet<>();
@Override
public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
mCallback = callback;
}
@Override
public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
return mExtendLifetimes;
}
@Override
public void setShouldManageLifetime(
@NonNull NotificationEntry entry,
boolean shouldManage) {
final boolean hasEntry = mManagedNotifs.contains(entry.getKey());
if (shouldManage) {
if (hasEntry) {
throw new RuntimeException("Already managing this entry: " + entry.getKey());
}
mManagedNotifs.add(entry.getKey());
} else {
if (!hasEntry) {
throw new RuntimeException("Not managing this entry: " + entry.getKey());
}
mManagedNotifs.remove(entry.getKey());
}
}
public void setExtendLifetimes(boolean extendLifetimes) {
mExtendLifetimes = extendLifetimes;
}
public NotificationSafeToRemoveCallback getCallback() {
return mCallback;
}
public boolean isManaging(String notificationKey) {
return mManagedNotifs.contains(notificationKey);
}
}
private NotificationEntry createNotification() {
Notification.Builder n = new Notification.Builder(mContext, "id")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
return new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
.setUid(TEST_UID)
.setId(mId++)
.setNotification(n.build())
.setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
.build();
}
private static DismissedByUserStats defaultStats(NotificationEntry entry) {
return new DismissedByUserStats(
DISMISSAL_SHADE,
DISMISS_SENTIMENT_NEUTRAL,
NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
}
}