blob: c80396d8292abfc59db4db2e460443f0cb77d777 [file] [log] [blame]
Adrian Roose18033c2017-01-17 15:22:49 -08001/*
2 * Copyright (C) 2014 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
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
Adrian Roose18033c2017-01-17 15:22:49 -080018
Tony Mak29996702018-11-26 16:23:34 +000019import static com.google.common.truth.Truth.assertThat;
20
Gustav Senntoneab53682018-11-01 16:30:23 +000021import static org.junit.Assert.assertFalse;
Julia Reynoldsfc640012018-02-21 12:25:27 -050022import static org.mockito.ArgumentMatchers.any;
Selim Cinekf868efe2017-02-01 11:43:18 -080023import static org.mockito.ArgumentMatchers.anyFloat;
24import static org.mockito.Mockito.doNothing;
25import static org.mockito.Mockito.doReturn;
Julia Reynoldsfc640012018-02-21 12:25:27 -050026import static org.mockito.Mockito.mock;
27import static org.mockito.Mockito.never;
Selim Cinekf868efe2017-02-01 11:43:18 -080028import static org.mockito.Mockito.spy;
Julia Reynoldsfc640012018-02-21 12:25:27 -050029import static org.mockito.Mockito.times;
30import static org.mockito.Mockito.verify;
31import static org.mockito.Mockito.when;
Selim Cinekf868efe2017-02-01 11:43:18 -080032
Julia Reynoldsfc640012018-02-21 12:25:27 -050033import android.app.AppOpsManager;
Gustav Senntoneab53682018-11-01 16:30:23 +000034import android.app.Notification;
35import android.app.PendingIntent;
36import android.app.RemoteInput;
37import android.content.Intent;
38import android.graphics.drawable.Icon;
39import android.service.notification.StatusBarNotification;
Jason Monkc429f692017-06-27 13:13:49 -040040import android.support.test.annotation.UiThreadTest;
41import android.support.test.filters.SmallTest;
42import android.support.test.runner.AndroidJUnit4;
Julia Reynoldsfc640012018-02-21 12:25:27 -050043import android.util.ArraySet;
Gustav Senntoneab53682018-11-01 16:30:23 +000044import android.util.Pair;
Julia Reynoldsfc640012018-02-21 12:25:27 -050045import android.view.NotificationHeaderView;
Jason Monkc429f692017-06-27 13:13:49 -040046import android.view.View;
47
Gustav Senntoneab53682018-11-01 16:30:23 +000048import com.android.systemui.R;
Jason Monkfba8faf2017-05-23 10:42:59 -040049import com.android.systemui.SysuiTestCase;
Ned Burnsf81c4c42019-01-07 14:10:43 -050050import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Gustav Senntoneab53682018-11-01 16:30:23 +000051import com.android.systemui.statusbar.policy.SmartReplyConstants;
Jason Monkfba8faf2017-05-23 10:42:59 -040052
Jason Monkc429f692017-06-27 13:13:49 -040053import org.junit.Before;
54import org.junit.Test;
55import org.junit.runner.RunWith;
Gustav Senntoneab53682018-11-01 16:30:23 +000056import org.mockito.Mock;
57import org.mockito.MockitoAnnotations;
58
59import java.util.ArrayList;
60import java.util.List;
Jason Monkc429f692017-06-27 13:13:49 -040061
Adrian Roose18033c2017-01-17 15:22:49 -080062@SmallTest
63@RunWith(AndroidJUnit4.class)
Jason Monkfba8faf2017-05-23 10:42:59 -040064public class NotificationContentViewTest extends SysuiTestCase {
Adrian Roose18033c2017-01-17 15:22:49 -080065
Gustav Senntoneab53682018-11-01 16:30:23 +000066 private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
67
Adrian Roose18033c2017-01-17 15:22:49 -080068 NotificationContentView mView;
Adrian Roose18033c2017-01-17 15:22:49 -080069
Gustav Senntoneab53682018-11-01 16:30:23 +000070 @Mock
71 SmartReplyConstants mSmartReplyConstants;
72 @Mock
73 StatusBarNotification mStatusBarNotification;
74 @Mock
75 Notification mNotification;
Ned Burnsf81c4c42019-01-07 14:10:43 -050076 NotificationEntry mEntry;
Gustav Senntoneab53682018-11-01 16:30:23 +000077 @Mock
78 RemoteInput mRemoteInput;
79 @Mock
80 RemoteInput mFreeFormRemoteInput;
81
82 private Icon mActionIcon;
83
84
Adrian Roose18033c2017-01-17 15:22:49 -080085 @Before
Selim Cinekf868efe2017-02-01 11:43:18 -080086 @UiThreadTest
Adrian Roose18033c2017-01-17 15:22:49 -080087 public void setup() {
Gustav Senntoneab53682018-11-01 16:30:23 +000088 MockitoAnnotations.initMocks(this);
89
Adrian Roose18033c2017-01-17 15:22:49 -080090 mView = new NotificationContentView(mContext, null);
Selim Cinekf868efe2017-02-01 11:43:18 -080091 ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
92 ExpandableNotificationRow mockRow = spy(row);
93 doNothing().when(mockRow).updateBackgroundAlpha(anyFloat());
94 doReturn(10).when(mockRow).getIntrinsicHeight();
95
96 mView.setContainingNotification(mockRow);
Adrian Roose18033c2017-01-17 15:22:49 -080097 mView.setHeights(10, 20, 30, 40);
98
99 mView.setContractedChild(createViewWithHeight(10));
100 mView.setExpandedChild(createViewWithHeight(20));
101 mView.setHeadsUpChild(createViewWithHeight(30));
102 mView.setAmbientChild(createViewWithHeight(40));
103
104 mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
105 mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
Gustav Senntoneab53682018-11-01 16:30:23 +0000106
Gustav Sennton761884c2018-11-19 17:40:19 +0000107 // Smart replies and actions
108 when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
Gustav Senntoneab53682018-11-01 16:30:23 +0000109 when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500110 mEntry = new NotificationEntry(mStatusBarNotification);
Gustav Senntoneab53682018-11-01 16:30:23 +0000111 when(mSmartReplyConstants.isEnabled()).thenReturn(true);
112 mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
Adrian Roose18033c2017-01-17 15:22:49 -0800113 }
114
115 private View createViewWithHeight(int height) {
116 View view = new View(mContext, null);
117 view.setMinimumHeight(height);
118 return view;
119 }
120
121 @Test
122 @UiThreadTest
123 public void animationStartType_getsClearedAfterUpdatingVisibilitiesWithoutAnimation() {
124 mView.setHeadsUp(true);
125 mView.setDark(true, false, 0);
126 mView.setDark(false, true, 0);
127 mView.setHeadsUpAnimatingAway(true);
Gustav Senntoneab53682018-11-01 16:30:23 +0000128 assertFalse(mView.isAnimatingVisibleType());
Adrian Roose18033c2017-01-17 15:22:49 -0800129 }
Julia Reynoldsfc640012018-02-21 12:25:27 -0500130
131 @Test
132 @UiThreadTest
133 public void testShowAppOpsIcons() {
134 NotificationHeaderView mockContracted = mock(NotificationHeaderView.class);
135 when(mockContracted.findViewById(com.android.internal.R.id.notification_header))
136 .thenReturn(mockContracted);
137 NotificationHeaderView mockExpanded = mock(NotificationHeaderView.class);
138 when(mockExpanded.findViewById(com.android.internal.R.id.notification_header))
139 .thenReturn(mockExpanded);
140 NotificationHeaderView mockHeadsUp = mock(NotificationHeaderView.class);
141 when(mockHeadsUp.findViewById(com.android.internal.R.id.notification_header))
142 .thenReturn(mockHeadsUp);
143 NotificationHeaderView mockAmbient = mock(NotificationHeaderView.class);
144 when(mockAmbient.findViewById(com.android.internal.R.id.notification_header))
145 .thenReturn(mockAmbient);
146
147 mView.setContractedChild(mockContracted);
148 mView.setExpandedChild(mockExpanded);
149 mView.setHeadsUpChild(mockHeadsUp);
150 mView.setAmbientChild(mockAmbient);
151
152 ArraySet<Integer> ops = new ArraySet<>();
153 ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
154 mView.showAppOpsIcons(ops);
155
156 verify(mockContracted, times(1)).showAppOpsIcons(ops);
157 verify(mockExpanded, times(1)).showAppOpsIcons(ops);
158 verify(mockAmbient, never()).showAppOpsIcons(ops);
159 verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
160 }
Gustav Senntoneab53682018-11-01 16:30:23 +0000161
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000162 private void setupAppGeneratedReplies(CharSequence[] smartReplies) {
163 setupAppGeneratedReplies(smartReplies, true /* allowSystemGeneratedReplies */);
Gustav Senntoneab53682018-11-01 16:30:23 +0000164 }
165
166 private void setupAppGeneratedReplies(
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000167 CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) {
Tony Mak29996702018-11-26 16:23:34 +0000168 PendingIntent pendingIntent =
169 PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
Gustav Senntoneab53682018-11-01 16:30:23 +0000170 Notification.Action action =
Tony Mak29996702018-11-26 16:23:34 +0000171 new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000172 when(mRemoteInput.getChoices()).thenReturn(smartReplies);
Gustav Senntoneab53682018-11-01 16:30:23 +0000173 Pair<RemoteInput, Notification.Action> remoteInputActionPair =
174 Pair.create(mRemoteInput, action);
175 when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
176
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000177 Notification.Action freeFormRemoteInputAction =
178 createActionBuilder("Freeform Test Action")
179 .setAllowGeneratedReplies(allowSystemGeneratedReplies)
180 .build();
Gustav Senntoneab53682018-11-01 16:30:23 +0000181 Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
182 Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
183 when(mNotification.findRemoteInputActionPair(true)).thenReturn(
184 freeFormRemoteInputActionPair);
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000185
186 when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
187 }
188
189 private void setupAppGeneratedSuggestions(
190 CharSequence[] smartReplies, List<Notification.Action> smartActions) {
191 setupAppGeneratedReplies(smartReplies);
192 when(mNotification.getContextualActions()).thenReturn(smartActions);
Gustav Senntoneab53682018-11-01 16:30:23 +0000193 }
194
195 @Test
Gustav Sennton1ee9c9d2019-01-29 17:13:36 +0000196 public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions() {
197 CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
198 List<Notification.Action> smartActions =
199 createActions(new String[] {"Test Action 1", "Test Action 2"});
200 setupAppGeneratedSuggestions(smartReplies, smartActions);
Gustav Senntoneab53682018-11-01 16:30:23 +0000201 when(mSmartReplyConstants.isEnabled()).thenReturn(false);
202
203 NotificationContentView.SmartRepliesAndActions repliesAndActions =
204 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
205
Tony Mak29996702018-11-26 16:23:34 +0000206 assertThat(repliesAndActions.smartReplies).isNull();
Gustav Sennton1ee9c9d2019-01-29 17:13:36 +0000207 assertThat(repliesAndActions.smartActions).isNull();
208 }
209
210 @Test
211 public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() {
Gustav Senntone255f902019-01-31 13:28:02 +0000212 mEntry.systemGeneratedSmartReplies =
213 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
Gustav Sennton1ee9c9d2019-01-29 17:13:36 +0000214 mEntry.systemGeneratedSmartActions =
215 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
216 when(mSmartReplyConstants.isEnabled()).thenReturn(false);
217
218 NotificationContentView.SmartRepliesAndActions repliesAndActions =
219 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
220
221 assertThat(repliesAndActions.smartReplies).isNull();
222 assertThat(repliesAndActions.smartActions).isNull();
Gustav Senntoneab53682018-11-01 16:30:23 +0000223 }
224
225 @Test
226 public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
227 CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
228 setupAppGeneratedReplies(smartReplies);
Gustav Senntoneab53682018-11-01 16:30:23 +0000229
230 NotificationContentView.SmartRepliesAndActions repliesAndActions =
231 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
232
Tony Mak29996702018-11-26 16:23:34 +0000233 assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
234 assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
235 assertThat(repliesAndActions.smartActions).isNull();
Gustav Senntoneab53682018-11-01 16:30:23 +0000236 }
237
238 @Test
239 public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
240 CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
Gustav Senntoneab53682018-11-01 16:30:23 +0000241 List<Notification.Action> smartActions =
242 createActions(new String[] {"Test Action 1", "Test Action 2"});
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000243 setupAppGeneratedSuggestions(smartReplies, smartActions);
Gustav Senntoneab53682018-11-01 16:30:23 +0000244
245 NotificationContentView.SmartRepliesAndActions repliesAndActions =
246 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
247
Tony Mak29996702018-11-26 16:23:34 +0000248 assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
249 assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
250 assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
251 assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
Gustav Senntoneab53682018-11-01 16:30:23 +0000252 }
253
254 @Test
255 public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
Gustav Senntoneab53682018-11-01 16:30:23 +0000256 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
257 // replies.
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000258 setupAppGeneratedReplies(null /* smartReplies */);
Gustav Senntoneab53682018-11-01 16:30:23 +0000259
Gustav Senntone255f902019-01-31 13:28:02 +0000260 mEntry.systemGeneratedSmartReplies =
261 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
Gustav Senntoneab53682018-11-01 16:30:23 +0000262 NotificationContentView.SmartRepliesAndActions repliesAndActions =
263 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
264
Gustav Senntone255f902019-01-31 13:28:02 +0000265 assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
266 mEntry.systemGeneratedSmartReplies);
Tony Mak29996702018-11-26 16:23:34 +0000267 assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
268 assertThat(repliesAndActions.smartActions).isNull();
Gustav Senntoneab53682018-11-01 16:30:23 +0000269 }
270
271 @Test
272 public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
Gustav Senntoneab53682018-11-01 16:30:23 +0000273 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
274 // replies.
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000275 setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
Gustav Senntoneab53682018-11-01 16:30:23 +0000276
Gustav Senntone255f902019-01-31 13:28:02 +0000277 mEntry.systemGeneratedSmartReplies =
Gustav Senntoneab53682018-11-01 16:30:23 +0000278 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
279 NotificationContentView.SmartRepliesAndActions repliesAndActions =
280 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
281
Tony Mak29996702018-11-26 16:23:34 +0000282 assertThat(repliesAndActions.smartReplies).isNull();
283 assertThat(repliesAndActions.smartActions).isNull();
Gustav Senntoneab53682018-11-01 16:30:23 +0000284 }
285
286 @Test
287 public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
288 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
289 // actions.
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000290 setupAppGeneratedReplies(null /* smartReplies */);
Gustav Senntoneab53682018-11-01 16:30:23 +0000291
292 mEntry.systemGeneratedSmartActions =
293 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
294 NotificationContentView.SmartRepliesAndActions repliesAndActions =
295 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
296
Tony Mak29996702018-11-26 16:23:34 +0000297 assertThat(repliesAndActions.smartReplies).isNull();
298 assertThat(repliesAndActions.smartActions.actions)
299 .isEqualTo(mEntry.systemGeneratedSmartActions);
300 assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
Gustav Senntoneab53682018-11-01 16:30:23 +0000301 }
302
303 @Test
304 public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
Gustav Senntoneab53682018-11-01 16:30:23 +0000305 CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
306 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
307 // replies.
Gustav Senntoneab53682018-11-01 16:30:23 +0000308 List<Notification.Action> appGenSmartActions =
309 createActions(new String[] {"Test Action 1", "Test Action 2"});
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000310 setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions);
Gustav Senntoneab53682018-11-01 16:30:23 +0000311
Gustav Senntone255f902019-01-31 13:28:02 +0000312 mEntry.systemGeneratedSmartReplies =
313 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
Gustav Senntoneab53682018-11-01 16:30:23 +0000314 mEntry.systemGeneratedSmartActions =
315 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
316
317 NotificationContentView.SmartRepliesAndActions repliesAndActions =
318 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
319
Tony Mak29996702018-11-26 16:23:34 +0000320 assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
321 assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
322 assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
323 assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
Gustav Senntoneab53682018-11-01 16:30:23 +0000324 }
325
Gustav Sennton761884c2018-11-19 17:40:19 +0000326 @Test
327 public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
328 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
329 // actions.
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000330 setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
Gustav Sennton761884c2018-11-19 17:40:19 +0000331 when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
Gustav Senntone255f902019-01-31 13:28:02 +0000332 mEntry.systemGeneratedSmartReplies =
333 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
Gustav Sennton761884c2018-11-19 17:40:19 +0000334 mEntry.systemGeneratedSmartActions =
335 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
Gustav Sennton79d22ae2018-12-17 16:45:46 +0000336
Gustav Sennton761884c2018-11-19 17:40:19 +0000337 NotificationContentView.SmartRepliesAndActions repliesAndActions =
338 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
339
Tony Mak29996702018-11-26 16:23:34 +0000340 assertThat(repliesAndActions.smartActions).isNull();
341 assertThat(repliesAndActions.smartReplies).isNull();
Gustav Sennton761884c2018-11-19 17:40:19 +0000342 }
343
Gustav Senntoneab53682018-11-01 16:30:23 +0000344 private Notification.Action.Builder createActionBuilder(String actionTitle) {
345 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
346 new Intent(TEST_ACTION), 0);
347 return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent);
348 }
349
350 private Notification.Action createAction(String actionTitle) {
351 return createActionBuilder(actionTitle).build();
352 }
353
354 private List<Notification.Action> createActions(String[] actionTitles) {
355 List<Notification.Action> actions = new ArrayList<>();
356 for (String title : actionTitles) {
357 actions.add(createAction(title));
358 }
359 return actions;
360 }
Adrian Roose18033c2017-01-17 15:22:49 -0800361}