1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar;
18
19import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
20
21import static org.junit.Assert.assertEquals;
22import static org.junit.Assert.assertFalse;
23import static org.junit.Assert.assertTrue;
24import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.ArgumentMatchers.anyInt;
26import static org.mockito.ArgumentMatchers.eq;
27import static org.mockito.Mockito.mock;
28import static org.mockito.Mockito.reset;
29import static org.mockito.Mockito.spy;
30import static org.mockito.Mockito.times;
31import static org.mockito.Mockito.verify;
32import static org.mockito.Mockito.when;
33
34import android.app.AppOpsManager;
35import android.app.NotificationChannel;
36import android.support.test.filters.SmallTest;
37import android.testing.AndroidTestingRunner;
38import android.testing.TestableLooper.RunWithLooper;
39import android.util.ArraySet;
40import android.view.NotificationHeaderView;
41import android.view.View;
42
43import com.android.systemui.SysuiTestCase;
44import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
45import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
46import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
47
48import org.junit.Assert;
49import org.junit.Before;
50import org.junit.Rule;
51import org.junit.Test;
52import org.junit.runner.RunWith;
53import org.mockito.Mock;
54import org.mockito.junit.MockitoJUnit;
55import org.mockito.junit.MockitoRule;
56
57import java.util.List;
58
59@SmallTest
60@RunWith(AndroidTestingRunner.class)
61@RunWithLooper(setAsMainLooper = true)
62public class ExpandableNotificationRowTest extends SysuiTestCase {
63
64    private ExpandableNotificationRow mGroupRow;
65
66    private NotificationTestHelper mNotificationTestHelper;
67    boolean mHeadsUpAnimatingAway = false;
68
69    @Rule public MockitoRule mockito = MockitoJUnit.rule();
70    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
71
72    @Before
73    public void setUp() throws Exception {
74        mNotificationTestHelper = new NotificationTestHelper(mContext);
75        mGroupRow = mNotificationTestHelper.createGroup();
76        mGroupRow.setHeadsUpAnimatingAwayListener(
77                animatingAway -> mHeadsUpAnimatingAway = animatingAway);
78        mDependency.injectTestDependency(
79                NotificationBlockingHelperManager.class,
80                mBlockingHelperManager);
81    }
82
83    @Test
84    public void testGroupSummaryNotShowingIconWhenPublic() {
85        mGroupRow.setSensitive(true, true);
86        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
87        assertTrue(mGroupRow.isSummaryWithChildren());
88        assertFalse(mGroupRow.isShowingIcon());
89    }
90
91    @Test
92    public void testNotificationHeaderVisibleWhenAnimating() {
93        mGroupRow.setSensitive(true, true);
94        mGroupRow.setHideSensitive(true, false, 0, 0);
95        mGroupRow.setHideSensitive(false, true, 0, 0);
96        assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
97                == View.VISIBLE);
98    }
99
100    @Test
101    public void testUserLockedResetEvenWhenNoChildren() {
102        mGroupRow.setUserLocked(true);
103        mGroupRow.removeAllChildren();
104        mGroupRow.setUserLocked(false);
105        assertFalse("The childrencontainer should not be userlocked but is, the state "
106                + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
107    }
108
109    @Test
110    public void testReinflatedOnDensityChange() {
111        mGroupRow.setUserLocked(true);
112        mGroupRow.removeAllChildren();
113        mGroupRow.setUserLocked(false);
114        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
115        mGroupRow.setChildrenContainer(mockContainer);
116        mGroupRow.onDensityOrFontScaleChanged();
117        verify(mockContainer).reInflateViews(any(), any());
118    }
119
120    @Test
121    public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception {
122        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
123        row.setSensitive(true, true);
124        row.setHideSensitive(true, false, 0, 0);
125        verify(row).updateShelfIconColor();
126    }
127
128    @Test
129    public void testIconColorShouldBeUpdatedWhenSettingDark() throws Exception {
130        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
131        row.setDark(true, false, 0);
132        verify(row).updateShelfIconColor();
133    }
134
135    @Test
136    public void testAboveShelfChangedListenerCalled() throws Exception {
137        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
138        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
139        row.setAboveShelfChangedListener(listener);
140        row.setHeadsUp(true);
141        verify(listener).onAboveShelfStateChanged(true);
142    }
143
144    @Test
145    public void testAboveShelfChangedListenerCalledPinned() throws Exception {
146        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
147        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
148        row.setAboveShelfChangedListener(listener);
149        row.setPinned(true);
150        verify(listener).onAboveShelfStateChanged(true);
151    }
152
153    @Test
154    public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception {
155        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
156        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
157        row.setAboveShelfChangedListener(listener);
158        row.setHeadsUpAnimatingAway(true);
159        verify(listener).onAboveShelfStateChanged(true);
160    }
161    @Test
162    public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception {
163        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
164        row.setHeadsUp(true);
165        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
166        row.setAboveShelfChangedListener(listener);
167        row.setAboveShelf(false);
168        verify(listener).onAboveShelfStateChanged(false);
169    }
170
171    @Test
172    public void testClickSound() throws Exception {
173        assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
174        mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */);
175        mGroupRow.setSecureStateProvider(()-> false);
176        assertFalse("Shouldn't play sounds when dark and trusted.",
177                mGroupRow.isSoundEffectsEnabled());
178        mGroupRow.setSecureStateProvider(()-> true);
179        assertTrue("Should always play sounds when not trusted.",
180                mGroupRow.isSoundEffectsEnabled());
181    }
182
183    @Test
184    public void testSetDismissed_longPressListenerRemoved() {
185        ExpandableNotificationRow.LongPressListener listener =
186                mock(ExpandableNotificationRow.LongPressListener.class);
187        mGroupRow.setLongPressListener(listener);
188        mGroupRow.doLongClickCallback(0,0);
189        verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
190                any(NotificationMenuRowPlugin.MenuItem.class));
191        reset(listener);
192
193        mGroupRow.setDismissed(true);
194        mGroupRow.doLongClickCallback(0,0);
195        verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
196                any(NotificationMenuRowPlugin.MenuItem.class));
197    }
198
199    @Test
200    public void testShowAppOps_noHeader() {
201        // public notification is custom layout - no header
202        mGroupRow.setSensitive(true, true);
203        mGroupRow.setAppOpsOnClickListener(null);
204        mGroupRow.showAppOpsIcons(null);
205    }
206
207    @Test
208    public void testShowAppOpsIcons_header() {
209        NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
210
211        NotificationContentView publicLayout = mock(NotificationContentView.class);
212        mGroupRow.setPublicLayout(publicLayout);
213        NotificationContentView privateLayout = mock(NotificationContentView.class);
214        mGroupRow.setPrivateLayout(privateLayout);
215        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
216        when(mockContainer.getNotificationChildCount()).thenReturn(1);
217        when(mockContainer.getHeaderView()).thenReturn(mockHeader);
218        mGroupRow.setChildrenContainer(mockContainer);
219
220        ArraySet<Integer> ops = new ArraySet<>();
221        ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
222        mGroupRow.showAppOpsIcons(ops);
223
224        verify(mockHeader, times(1)).showAppOpsIcons(ops);
225        verify(privateLayout, times(1)).showAppOpsIcons(ops);
226        verify(publicLayout, times(1)).showAppOpsIcons(ops);
227
228    }
229
230    @Test
231    public void testAppOpsOnClick() {
232        ExpandableNotificationRow.OnAppOpsClickListener l = mock(
233                ExpandableNotificationRow.OnAppOpsClickListener.class);
234        View view = mock(View.class);
235
236        mGroupRow.setAppOpsOnClickListener(l);
237
238        mGroupRow.getAppOpsOnClickListener().onClick(view);
239        verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
240    }
241
242    @Test
243    public void testHeadsUpAnimatingAwayListener() {
244        mGroupRow.setHeadsUpAnimatingAway(true);
245        Assert.assertEquals(true, mHeadsUpAnimatingAway);
246        mGroupRow.setHeadsUpAnimatingAway(false);
247        Assert.assertEquals(false, mHeadsUpAnimatingAway);
248    }
249
250    @Test
251    public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
252        when(mBlockingHelperManager.perhapsShowBlockingHelper(
253                eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
254
255        assertFalse(
256                mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
257    }
258
259    @Test
260    public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
261        ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
262        when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
263                .thenReturn(true);
264
265        assertTrue(
266                childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
267
268        verify(mBlockingHelperManager, times(1))
269                .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
270        verify(mBlockingHelperManager, times(0))
271                .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
272    }
273
274    @Test
275    public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
276        mGroupRow.setBlockingHelperShowing(true);
277        assertTrue(mGroupRow.isBlockingHelperShowing());
278
279        mGroupRow.setBlockingHelperShowing(false);
280        assertFalse(mGroupRow.isBlockingHelperShowing());
281    }
282
283    @Test
284    public void testGetNumUniqueChildren_defaultChannel() {
285        assertEquals(1, mGroupRow.getNumUniqueChannels());
286    }
287
288    @Test
289    public void testGetNumUniqueChildren_multiChannel() {
290        List<ExpandableNotificationRow> childRows =
291                mGroupRow.getChildrenContainer().getNotificationChildren();
292        // Give each child a unique channel id/name.
293        int i = 0;
294        for (ExpandableNotificationRow childRow : childRows) {
295            childRow.getEntry().channel =
296                    new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT);
297            i++;
298        }
299
300        assertEquals(3, mGroupRow.getNumUniqueChannels());
301    }
302
303    @Test
304    public void testIconScrollXAfterTranslationAndReset() throws Exception {
305        mGroupRow.setTranslation(50);
306        assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX());
307
308        mGroupRow.resetTranslation();
309        assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
310    }
311}
312