Merge "Move inflation-related NEM tests to their own file"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 234ab93..5008133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -482,6 +482,7 @@
* once per notification as the packageInfo can't technically change for a notification row.
*/
private void cacheIsSystemNotification() {
+ //TODO: This probably shouldn't be in ExpandableNotificationRow
if (mEntry != null && mEntry.mIsSystemNotification == null) {
if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) {
// Run async task once, only if it hasn't already been executed. Note this is
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 6a6e5c8..98f12ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -52,73 +52,55 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
-import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
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.RankingBuilder;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.StatusBarIconView;
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.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.RowContentBindParams;
-import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+import com.android.systemui.statusbar.notification.row.NotificationEntryManagerInflationTest;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
-import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
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.util.leak.LeakDetector;
-import com.android.systemui.util.time.FakeSystemClock;
-import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+/**
+ * Unit tests for {@link NotificationEntryManager}. This test will not test any interactions with
+ * inflation. Instead, for functional inflation tests, see
+ * {@link NotificationEntryManagerInflationTest}.
+ */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
@@ -129,17 +111,11 @@
@Mock private NotificationPresenter mPresenter;
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
- @Mock private NotificationListContainer mListContainer;
@Mock private NotificationEntryListener mEntryListener;
@Mock private NotificationRemoveInterceptor mRemoveInterceptor;
- @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RankingMap mRankingMap;
- @Mock private RemoteInputController mRemoteInputController;
- @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
- @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationGroupManager mGroupManager;
- @Mock private NotificationGutsManager mGutsManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private RowInflaterTask mAsyncInflationTask;
@@ -147,18 +123,12 @@
@Mock private FeatureFlags mFeatureFlags;
@Mock private LeakDetector mLeakDetector;
@Mock private NotificationMediaManager mNotificationMediaManager;
- @Mock private ExpandableNotificationRowComponent.Builder
- mExpandableNotificationRowComponentBuilder;
- @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent;
- @Mock private FalsingManager mFalsingManager;
- @Mock private KeyguardBypassController mKeyguardBypassController;
- @Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private NotificationRowBinder mNotificationRowBinder;
private int mId;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
- private TestableNotificationEntryManager mEntryManager;
- private CountDownLatch mCountDownLatch;
+ private NotificationEntryManager mEntryManager;
private void setUserSentiment(String key, int sentiment) {
doAnswer(invocationOnMock -> {
@@ -202,41 +172,16 @@
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(SmartReplyController.class);
- mCountDownLatch = new CountDownLatch(1);
-
allowTestableLooperAsMainThread();
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(TestableLooper.get(this).getLooper()));
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- when(mListContainer.getViewParentForNotification(any())).thenReturn(
- new FrameLayout(mContext));
mEntry = createNotification();
mSbn = mEntry.getSbn();
- mEntry.expandedIcon = mock(StatusBarIconView.class);
-
- RowContentBindStage bindStage = mock(RowContentBindStage.class);
- when(bindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
- NotificationRowBinderImpl notificationRowBinder =
- new NotificationRowBinderImpl(mContext,
- new NotificationMessagingUtil(mContext),
- mRemoteInputManager,
- mLockscreenUserManager,
- mock(NotifBindPipeline.class),
- bindStage,
- true, /* allowLongPress */
- mKeyguardBypassController,
- mStatusBarStateController,
- mGroupManager,
- mGutsManager,
- mNotificationInterruptionStateProvider,
- RowInflaterTask::new,
- mExpandableNotificationRowComponentBuilder);
-
when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
- mEntryManager = new TestableNotificationEntryManager(
+ mEntryManager = new NotificationEntryManager(
mLogger,
mGroupManager,
new NotificationRankingManager(
@@ -250,7 +195,7 @@
mock(HighPriorityProvider.class)),
mEnvironment,
mFeatureFlags,
- () -> notificationRowBinder,
+ () -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class)
@@ -259,152 +204,45 @@
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
- notificationRowBinder.setUpWithPresenter(mPresenter, mListContainer, mBindCallback);
- notificationRowBinder.setInflationCallback(mEntryManager);
- notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
-
- setUserSentiment(
- mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
-
- ArgumentCaptor<ExpandableNotificationRow> viewCaptor =
- ArgumentCaptor.forClass(ExpandableNotificationRow.class);
- when(mExpandableNotificationRowComponentBuilder
- .expandableNotificationRow(viewCaptor.capture()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .notificationEntry(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .onDismissRunnable(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .inflationCallback(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .rowContentBindStage(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .onExpandClickListener(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
-
- when(mExpandableNotificationRowComponentBuilder.build())
- .thenReturn(mExpandableNotificationRowComponent);
- when(mExpandableNotificationRowComponent.getExpandableNotificationRowController())
- .thenAnswer((Answer<ExpandableNotificationRowController>) invocation ->
- new ExpandableNotificationRowController(
- viewCaptor.getValue(),
- mock(ActivatableNotificationViewController.class),
- mNotificationMediaManager,
- mock(PluginManager.class),
- new FakeSystemClock(),
- "FOOBAR", "FOOBAR",
- mKeyguardBypassController,
- mGroupManager,
- bindStage,
- mock(NotificationLogger.class),
- mHeadsUpManager,
- mPresenter,
- mStatusBarStateController,
- mEntryManager,
- mGutsManager,
- true,
- null,
- mFalsingManager
- ));
+ setUserSentiment(mSbn.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
}
- @After
- public void tearDown() {
- // CLEAN UP inflation tasks so they don't callback in a future test
- mEntry.abortTask();
- }
-
- // TODO: These tests are closer to functional tests and we should move them to their own file.
- // and also strip some of the verifies that make the test too complex
@Test
- @Ignore
- public void testAddNotification() throws Exception {
- TestableLooper.get(this).processAllMessages();
-
- doAnswer(invocation -> {
- mCountDownLatch.countDown();
- return null;
- }).when(mBindCallback).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.
+ public void testAddNotification_setsUserSentiment() {
mEntryManager.addNotification(mSbn, mRankingMap);
- TestableLooper.get(this).processMessages(1);
- assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS));
- assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
- // Check that no inflation error occurred.
- verify(mEntryListener, never()).onInflationError(any(), any());
-
- // Row inflation:
ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
NotificationEntry.class);
- verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
+ verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
NotificationEntry entry = entryCaptor.getValue();
- verify(mRemoteInputManager).bindRow(entry.getRow());
- // Row content inflation:
- verify(mEntryListener).onNotificationAdded(entry);
- verify(mPresenter).updateNotificationViews();
-
- assertEquals(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()), entry);
- assertNotNull(entry.getRow());
- assertEquals(mEntry.getUserSentiment(),
- Ranking.USER_SENTIMENT_NEUTRAL);
+ assertEquals(entry.getUserSentiment(), Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
- @Ignore
- public void testUpdateNotification() throws Exception {
- TestableLooper.get(this).processAllMessages();
-
+ public void testUpdateNotification_updatesUserSentiment() {
mEntryManager.addActiveNotificationForTest(mEntry);
-
setUserSentiment(
mEntry.getKey(), Ranking.USER_SENTIMENT_NEGATIVE);
mEntryManager.updateNotification(mSbn, mRankingMap);
- TestableLooper.get(this).processMessages(1);
- // Wait for content update.
- assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
- verify(mEntryListener, never()).onInflationError(any(), any());
-
- verify(mEntryListener).onPreEntryUpdated(mEntry);
- verify(mPresenter).updateNotificationViews();
- verify(mEntryListener).onPostEntryUpdated(mEntry);
-
- assertNotNull(mEntry.getRow());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
- mEntry.getUserSentiment());
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, mEntry.getUserSentiment());
}
@Test
- @Ignore
public void testUpdateNotification_prePostEntryOrder() throws Exception {
TestableLooper.get(this).processAllMessages();
mEntryManager.addActiveNotificationForTest(mEntry);
mEntryManager.updateNotification(mSbn, mRankingMap);
- TestableLooper.get(this).processMessages(1);
- // Wait for content update.
- assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
-
- verify(mEntryListener, never()).onInflationError(any(), any());
// Ensure that update callbacks happen in correct order
InOrder order = inOrder(mEntryListener, mPresenter, mEntryListener);
order.verify(mEntryListener).onPreEntryUpdated(mEntry);
order.verify(mPresenter).updateNotificationViews();
order.verify(mEntryListener).onPostEntryUpdated(mEntry);
-
- assertNotNull(mEntry.getRow());
}
@Test
@@ -414,8 +252,6 @@
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
- verify(mEntryListener, never()).onInflationError(any(), any());
-
verify(mPresenter).updateNotificationViews();
verify(mEntryListener).onEntryRemoved(
eq(mEntry), any(), eq(false) /* removedByUser */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
new file mode 100644
index 0000000..ac40808
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2020 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.row;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+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.Notification;
+import android.os.Handler;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
+import com.android.systemui.statusbar.notification.NotificationClicker;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Functional tests for notification inflation from {@link NotificationEntryManager}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class NotificationEntryManagerInflationTest extends SysuiTestCase {
+
+ private static final String TEST_TITLE = "Title";
+ private static final String TEST_TEXT = "Text";
+ private static final long TIMEOUT_TIME = 10000;
+ private static final Runnable TIMEOUT_RUNNABLE = () -> {
+ throw new RuntimeException("Timed out waiting to inflate");
+ };
+
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationEntryManager.KeyguardEnvironment mEnvironment;
+ @Mock private NotificationListContainer mListContainer;
+ @Mock private NotificationEntryListener mEntryListener;
+ @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
+ @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotificationGutsManager mGutsManager;
+ @Mock private NotificationRemoteInputManager mRemoteInputManager;
+ @Mock private NotificationMediaManager mNotificationMediaManager;
+ @Mock private ExpandableNotificationRowComponent.Builder
+ mExpandableNotificationRowComponentBuilder;
+ @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent;
+ @Mock private FalsingManager mFalsingManager;
+ @Mock private KeyguardBypassController mKeyguardBypassController;
+ @Mock private StatusBarStateController mStatusBarStateController;
+
+ @Mock private NotificationGroupManager mGroupManager;
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private LeakDetector mLeakDetector;
+
+ @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
+ @Mock private NotificationRowComponent.Builder mNotificationRowComponentBuilder;
+
+ private StatusBarNotification mSbn;
+ private NotificationListenerService.RankingMap mRankingMap;
+ private NotificationEntryManager mEntryManager;
+ private NotificationRowBinderImpl mRowBinder;
+ private Handler mHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mDependency.injectMockDependency(SmartReplyController.class);
+
+ mHandler = Handler.createAsync(TestableLooper.get(this).getLooper());
+
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle(TEST_TITLE)
+ .setContentText(TEST_TEXT)
+ .build();
+ mSbn = new SbnBuilder()
+ .setNotification(notification)
+ .build();
+
+ when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
+ when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
+
+ mEntryManager = new NotificationEntryManager(
+ mock(NotificationEntryManagerLogger.class),
+ mGroupManager,
+ new NotificationRankingManager(
+ () -> mock(NotificationMediaManager.class),
+ mGroupManager,
+ mHeadsUpManager,
+ mock(NotificationFilter.class),
+ mock(NotificationEntryManagerLogger.class),
+ mock(NotificationSectionsFeatureManager.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class)),
+ mEnvironment,
+ mFeatureFlags,
+ () -> mRowBinder,
+ () -> mRemoteInputManager,
+ mLeakDetector,
+ mock(ForegroundServiceDismissalFeatureController.class)
+ );
+
+ NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
+ NotifBindPipeline pipeline = new NotifBindPipeline(
+ mEntryManager,
+ mock(NotifBindPipelineLogger.class));
+ NotificationContentInflater binder = new NotificationContentInflater(
+ cache,
+ mRemoteInputManager,
+ () -> mock(SmartReplyConstants.class),
+ () -> mock(SmartReplyController.class));
+ RowContentBindStage stage = new RowContentBindStage(
+ binder,
+ mock(NotifInflationErrorManager.class),
+ mock(RowContentBindStageLogger.class));
+ pipeline.setStage(stage);
+
+ ArgumentCaptor<ExpandableNotificationRow> viewCaptor =
+ ArgumentCaptor.forClass(ExpandableNotificationRow.class);
+ when(mExpandableNotificationRowComponentBuilder
+ .expandableNotificationRow(viewCaptor.capture()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
+ .notificationEntry(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
+ .onDismissRunnable(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
+ .inflationCallback(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
+ .rowContentBindStage(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
+ .onExpandClickListener(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+
+ when(mExpandableNotificationRowComponentBuilder.build())
+ .thenReturn(mExpandableNotificationRowComponent);
+ when(mExpandableNotificationRowComponent.getExpandableNotificationRowController())
+ .thenAnswer((Answer<ExpandableNotificationRowController>) invocation ->
+ new ExpandableNotificationRowController(
+ viewCaptor.getValue(),
+ mock(ActivatableNotificationViewController.class),
+ mNotificationMediaManager,
+ mock(PluginManager.class),
+ new FakeSystemClock(),
+ "FOOBAR", "FOOBAR",
+ mKeyguardBypassController,
+ mGroupManager,
+ stage,
+ mock(NotificationLogger.class),
+ mHeadsUpManager,
+ mPresenter,
+ mStatusBarStateController,
+ mEntryManager,
+ mGutsManager,
+ true,
+ null,
+ mFalsingManager
+ ));
+
+ when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
+ .thenReturn(mNotificationRowComponentBuilder);
+ when(mNotificationRowComponentBuilder.build()).thenReturn(
+ () -> mActivatableNotificationViewController);
+
+ mRowBinder = new NotificationRowBinderImpl(
+ mContext,
+ new NotificationMessagingUtil(mContext),
+ mRemoteInputManager,
+ mLockscreenUserManager,
+ pipeline,
+ stage,
+ true, /* allowLongPress */
+ mock(KeyguardBypassController.class),
+ mock(StatusBarStateController.class),
+ mGroupManager,
+ mGutsManager,
+ mNotificationInterruptionStateProvider,
+ RowInflaterTask::new,
+ mExpandableNotificationRowComponentBuilder);
+
+ mEntryManager.setUpWithPresenter(mPresenter);
+ mEntryManager.addNotificationEntryListener(mEntryListener);
+
+ mRowBinder.setUpWithPresenter(mPresenter, mListContainer, mBindCallback);
+ mRowBinder.setInflationCallback(mEntryManager);
+ mRowBinder.setNotificationClicker(mock(NotificationClicker.class));
+
+ Ranking ranking = new Ranking();
+ ranking.populate(
+ mSbn.getKey(),
+ 0,
+ false,
+ 0,
+ 0,
+ IMPORTANCE_DEFAULT,
+ null,
+ null,
+ null,
+ null,
+ null,
+ true,
+ Ranking.USER_SENTIMENT_NEUTRAL,
+ false,
+ -1,
+ false,
+ null,
+ null,
+ false,
+ false,
+ false);
+ mRankingMap = new NotificationListenerService.RankingMap(new Ranking[] {ranking});
+
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ @After
+ public void cleanUp() {
+ // Don't leave anything on main thread
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ @Test
+ public void testAddNotification() {
+ // WHEN a notification is added
+ mEntryManager.addNotification(mSbn, mRankingMap);
+ ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+ NotificationEntry.class);
+ verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
+ NotificationEntry entry = entryCaptor.getValue();
+
+ // Wait for inflation
+ // row inflation, system notification, remote views, contracted view
+ waitForMessages(4);
+
+ // THEN the notification has its row inflated
+ assertNotNull(entry.getRow());
+ assertNotNull(entry.getRow().getPrivateLayout().getContractedChild());
+
+ // THEN inflation callbacks are called
+ verify(mBindCallback).onBindRow(eq(entry), any(), eq(mSbn), any());
+ verify(mEntryListener, never()).onInflationError(any(), any());
+ verify(mEntryListener).onEntryInflated(entry);
+ verify(mEntryListener).onNotificationAdded(entry);
+
+ // THEN the notification is active
+ assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
+
+ // THEN we update the presenter
+ verify(mPresenter).updateNotificationViews();
+ }
+
+ @Test
+ public void testUpdateNotification() {
+ // GIVEN a notification already added
+ mEntryManager.addNotification(mSbn, mRankingMap);
+ ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+ NotificationEntry.class);
+ verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
+ NotificationEntry entry = entryCaptor.getValue();
+ waitForMessages(4);
+
+ Mockito.reset(mEntryListener);
+ Mockito.reset(mPresenter);
+
+ // WHEN the notification is updated
+ mEntryManager.updateNotification(mSbn, mRankingMap);
+
+ // Wait for inflation
+ // remote views, contracted view
+ waitForMessages(2);
+
+ // THEN the notification has its row and inflated
+ assertNotNull(entry.getRow());
+
+ // THEN inflation callbacks are called
+ verify(mEntryListener, never()).onInflationError(any(), any());
+ verify(mEntryListener).onEntryReinflated(entry);
+
+ // THEN we update the presenter
+ verify(mPresenter).updateNotificationViews();
+ }
+
+ /**
+ * Wait for a certain number of messages to finish before continuing, timing out if they never
+ * occur.
+ *
+ * As part of the inflation pipeline, the main thread is forced to deal with several callbacks
+ * due to the nature of the API used (generally because they're {@link android.os.AsyncTask}
+ * callbacks). In order, these are
+ *
+ * 1) Callback after row inflation. See {@link RowInflaterTask}.
+ * 2) Callback checking if row is system notification. See
+ * {@link ExpandableNotificationRow#setEntry}
+ * 3) Callback after remote views are created. See
+ * {@link NotificationContentInflater.AsyncInflationTask}.
+ * 4-6) Callback after each content view is inflated/rebound from remote view. See
+ * {@link NotificationContentInflater#applyRemoteView} and {@link InflationFlag}.
+ *
+ * Depending on the test, only some of these will be necessary. For example, generally, not
+ * every content view is inflated or the row may not be inflated if one already exists.
+ *
+ * Currently, the burden is on the developer to figure these out until we have a much more
+ * test-friendly way of executing inflation logic (i.e. pass in an executor).
+ */
+ private void waitForMessages(int numMessages) {
+ mHandler.postDelayed(TIMEOUT_RUNNABLE, TIMEOUT_TIME);
+ TestableLooper.get(this).processMessages(numMessages);
+ mHandler.removeCallbacks(TIMEOUT_RUNNABLE);
+ }
+
+}