/* * 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.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.google.android.collect.Lists; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Mock private NotificationPresenter mPresenter; @Mock private NotificationData mNotificationData; @Spy private FakeListContainer mListContainer = new FakeListContainer(); // Dependency mocks: @Mock private NotificationEntryManager mEntryManager; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGroupManager mGroupManager; @Mock private VisualStabilityManager mVisualStabilityManager; private NotificationViewHierarchyManager mViewHierarchyManager; private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);; @Before public void setUp() { MockitoAnnotations.initMocks(this); mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); mViewHierarchyManager = new NotificationViewHierarchyManager(mContext); mViewHierarchyManager.setUpWithPresenter(mPresenter, mEntryManager, mListContainer); } private NotificationData.Entry createEntry() throws Exception { ExpandableNotificationRow row = mHelper.createRow(); NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification()); entry.row = row; return entry; } @Test public void testNotificationsBecomingBundled() throws Exception { // Tests 3 top level notifications becoming a single bundled notification with |entry0| as // the summary. NotificationData.Entry entry0 = createEntry(); NotificationData.Entry entry1 = createEntry(); NotificationData.Entry entry2 = createEntry(); // Set up the prior state to look like three top level notifications. mListContainer.addContainerView(entry0.row); mListContainer.addContainerView(entry1.row); mListContainer.addContainerView(entry2.row); when(mNotificationData.getActiveNotifications()).thenReturn( Lists.newArrayList(entry0, entry1, entry2)); // Set up group manager to report that they should be bundled now. when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false); when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true); when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true); when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row); when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row); // Run updateNotifications - the view hierarchy should be reorganized. mViewHierarchyManager.updateNotificationViews(); verify(mListContainer).notifyGroupChildAdded(entry1.row); verify(mListContainer).notifyGroupChildAdded(entry2.row); assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows)); } @Test public void testNotificationsBecomingUnbundled() throws Exception { // Tests a bundled notification becoming three top level notifications. NotificationData.Entry entry0 = createEntry(); NotificationData.Entry entry1 = createEntry(); NotificationData.Entry entry2 = createEntry(); entry0.row.addChildNotification(entry1.row); entry0.row.addChildNotification(entry2.row); // Set up the prior state to look like one top level notification. mListContainer.addContainerView(entry0.row); when(mNotificationData.getActiveNotifications()).thenReturn( Lists.newArrayList(entry0, entry1, entry2)); // Set up group manager to report that they should not be bundled now. when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false); when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(false); when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(false); // Run updateNotifications - the view hierarchy should be reorganized. mViewHierarchyManager.updateNotificationViews(); verify(mListContainer).notifyGroupChildRemoved( entry1.row, entry0.row.getChildrenContainer()); verify(mListContainer).notifyGroupChildRemoved( entry2.row, entry0.row.getChildrenContainer()); assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows)); } @Test public void testNotificationsBecomingSuppressed() throws Exception { // Tests two top level notifications becoming a suppressed summary and a child. NotificationData.Entry entry0 = createEntry(); NotificationData.Entry entry1 = createEntry(); entry0.row.addChildNotification(entry1.row); // Set up the prior state to look like a top level notification. mListContainer.addContainerView(entry0.row); when(mNotificationData.getActiveNotifications()).thenReturn( Lists.newArrayList(entry0, entry1)); // Set up group manager to report a suppressed summary now. when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false); when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(false); when(mGroupManager.isSummaryOfSuppressedGroup(entry0.notification)).thenReturn(true); // Run updateNotifications - the view hierarchy should be reorganized. mViewHierarchyManager.updateNotificationViews(); verify(mListContainer).notifyGroupChildRemoved( entry1.row, entry0.row.getChildrenContainer()); assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows)); assertEquals(View.GONE, entry0.row.getVisibility()); assertEquals(View.VISIBLE, entry1.row.getVisibility()); } @Test public void testUpdateNotificationViews_appOps() throws Exception { NotificationData.Entry entry0 = createEntry(); entry0.row = spy(entry0.row); when(mNotificationData.getActiveNotifications()).thenReturn( Lists.newArrayList(entry0)); mListContainer.addContainerView(entry0.row); mViewHierarchyManager.updateNotificationViews(); verify(entry0.row, times(1)).showAppOpsIcons(any()); } private class FakeListContainer implements NotificationListContainer { final LinearLayout mLayout = new LinearLayout(mContext); final List mRows = Lists.newArrayList(); @Override public void setChildTransferInProgress(boolean childTransferInProgress) {} @Override public void changeViewPosition(View child, int newIndex) { mRows.remove(child); mRows.add(newIndex, child); } @Override public void notifyGroupChildAdded(View row) {} @Override public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {} @Override public void generateAddAnimation(View child, boolean fromMoreCard) {} @Override public void generateChildOrderChangedEvent() {} @Override public int getContainerChildCount() { return mRows.size(); } @Override public View getContainerChildAt(int i) { return mRows.get(i); } @Override public void removeContainerView(View v) { mLayout.removeView(v); mRows.remove(v); } @Override public void addContainerView(View v) { mLayout.addView(v); mRows.add(v); } @Override public void setMaxDisplayedNotifications(int maxNotifications) {} @Override public void snapViewIfNeeded(ExpandableNotificationRow row) {} @Override public ViewGroup getViewParentForNotification(NotificationData.Entry entry) { return null; } @Override public void onHeightChanged(ExpandableView view, boolean animate) {} @Override public void resetExposedMenuView(boolean animate, boolean force) {} @Override public NotificationSwipeActionHelper getSwipeActionHelper() { return null; } @Override public void cleanUpViewState(View view) {} @Override public boolean isInVisibleLocation(ExpandableNotificationRow row) { return true; } @Override public void setChildLocationsChangedListener( NotificationLogger.OnChildLocationsChangedListener listener) {} @Override public boolean hasPulsingNotifications() { return false; } } }