blob: d0b6d0061166c567f94d450e1f4aea5d8ed267ae [file] [log] [blame]
Tony Mak43a899f2018-12-07 18:30:51 +00001/**
2 * Copyright (C) 2018 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 */
16package android.ext.services.notification;
17
18import static com.google.common.truth.Truth.assertAbout;
19import static com.google.common.truth.Truth.assertThat;
20
21import static org.mockito.ArgumentMatchers.any;
Tony Mak40d5ce02019-02-11 20:38:26 +000022import static org.mockito.ArgumentMatchers.argThat;
Tony Mak43a899f2018-12-07 18:30:51 +000023import static org.mockito.Mockito.never;
Tony Mak40d5ce02019-02-11 20:38:26 +000024import static org.mockito.Mockito.times;
Tony Mak43a899f2018-12-07 18:30:51 +000025import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
27
28import android.annotation.NonNull;
29import android.app.Notification;
Tony Make1a27ac2019-01-31 16:32:19 +000030import android.app.NotificationChannel;
31import android.app.NotificationManager;
32import android.app.PendingIntent;
Tony Mak43a899f2018-12-07 18:30:51 +000033import android.app.Person;
Tony Make1a27ac2019-01-31 16:32:19 +000034import android.app.RemoteInput;
Tony Mak43a899f2018-12-07 18:30:51 +000035import android.content.Context;
Tony Make1a27ac2019-01-31 16:32:19 +000036import android.content.Intent;
37import android.content.pm.IPackageManager;
38import android.graphics.drawable.Icon;
Tony Mak43a899f2018-12-07 18:30:51 +000039import android.os.Process;
Tony Make94e0782018-12-14 11:57:54 +080040import android.service.notification.NotificationAssistantService;
Tony Mak43a899f2018-12-07 18:30:51 +000041import android.service.notification.StatusBarNotification;
42import android.support.test.InstrumentationRegistry;
43import android.support.test.runner.AndroidJUnit4;
Tony Makae85aae2019-01-09 15:59:56 +000044import android.view.textclassifier.ConversationAction;
Tony Mak43a899f2018-12-07 18:30:51 +000045import android.view.textclassifier.ConversationActions;
46import android.view.textclassifier.TextClassificationManager;
47import android.view.textclassifier.TextClassifier;
Tony Make94e0782018-12-14 11:57:54 +080048import android.view.textclassifier.TextClassifierEvent;
Tony Mak43a899f2018-12-07 18:30:51 +000049
50import com.google.common.truth.FailureStrategy;
51import com.google.common.truth.Subject;
52import com.google.common.truth.SubjectFactory;
53
54import org.junit.Before;
55import org.junit.Test;
56import org.junit.runner.RunWith;
57import org.mockito.ArgumentCaptor;
Tony Mak40d5ce02019-02-11 20:38:26 +000058import org.mockito.ArgumentMatcher;
Tony Mak43a899f2018-12-07 18:30:51 +000059import org.mockito.Mock;
60import org.mockito.MockitoAnnotations;
61
62import java.time.Instant;
63import java.time.ZoneOffset;
64import java.time.ZonedDateTime;
Tony Make94e0782018-12-14 11:57:54 +080065import java.util.Arrays;
Tony Mak43a899f2018-12-07 18:30:51 +000066import java.util.Collections;
67import java.util.List;
68import java.util.Objects;
69
70import javax.annotation.Nullable;
71
72@RunWith(AndroidJUnit4.class)
Tony Make1a27ac2019-01-31 16:32:19 +000073public class SmartActionsHelperTest {
Tony Make94e0782018-12-14 11:57:54 +080074 private static final String NOTIFICATION_KEY = "key";
75 private static final String RESULT_ID = "id";
Tony Mak40d5ce02019-02-11 20:38:26 +000076 private static final float SCORE = 0.7f;
77 private static final CharSequence SMART_REPLY = "Home";
Tony Makae85aae2019-01-09 15:59:56 +000078 private static final ConversationAction REPLY_ACTION =
79 new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
Tony Mak40d5ce02019-02-11 20:38:26 +000080 .setTextReply(SMART_REPLY)
81 .setConfidenceScore(SCORE)
Tony Make1a27ac2019-01-31 16:32:19 +000082 .build();
83 private static final String MESSAGE = "Where are you?";
84
85 @Mock
86 IPackageManager mIPackageManager;
87 @Mock
88 private TextClassifier mTextClassifier;
89 @Mock
90 private StatusBarNotification mStatusBarNotification;
91 @Mock
92 private SmsHelper mSmsHelper;
Tony Make94e0782018-12-14 11:57:54 +080093
94 private SmartActionsHelper mSmartActionsHelper;
Tony Mak43a899f2018-12-07 18:30:51 +000095 private Context mContext;
Tony Mak43a899f2018-12-07 18:30:51 +000096 private Notification.Builder mNotificationBuilder;
97 private AssistantSettings mSettings;
98
99 @Before
100 public void setup() {
101 MockitoAnnotations.initMocks(this);
102 mContext = InstrumentationRegistry.getTargetContext();
103
104 mContext.getSystemService(TextClassificationManager.class)
105 .setTextClassifier(mTextClassifier);
106 when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
Tony Make94e0782018-12-14 11:57:54 +0800107 .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
Tony Mak43a899f2018-12-07 18:30:51 +0000108
Tony Mak43a899f2018-12-07 18:30:51 +0000109 when(mStatusBarNotification.getPackageName()).thenReturn("random.app");
110 when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle());
Tony Make94e0782018-12-14 11:57:54 +0800111 when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY);
Tony Mak43a899f2018-12-07 18:30:51 +0000112 mNotificationBuilder = new Notification.Builder(mContext, "channel");
113 mSettings = AssistantSettings.createForTesting(
114 null, null, Process.myUserHandle().getIdentifier(), null);
115 mSettings.mGenerateActions = true;
116 mSettings.mGenerateReplies = true;
Tony Make94e0782018-12-14 11:57:54 +0800117 mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
Tony Mak43a899f2018-12-07 18:30:51 +0000118 }
119
120 @Test
Tony Make1a27ac2019-01-31 16:32:19 +0000121 public void testSuggest_notMessageNotification() {
122 Notification notification = mNotificationBuilder.setContentText(MESSAGE).build();
123 when(mStatusBarNotification.getNotification()).thenReturn(notification);
124
125 mSmartActionsHelper.suggest(createNotificationEntry());
126
127 verify(mTextClassifier, never())
128 .suggestConversationActions(any(ConversationActions.Request.class));
Tony Mak43a899f2018-12-07 18:30:51 +0000129 }
130
131 @Test
Tony Make1a27ac2019-01-31 16:32:19 +0000132 public void testSuggest_noInlineReply() {
133 Notification notification =
134 mNotificationBuilder
135 .setContentText(MESSAGE)
136 .setCategory(Notification.CATEGORY_MESSAGE)
137 .build();
138 when(mStatusBarNotification.getNotification()).thenReturn(notification);
139
140 ConversationActions.Request request = runSuggestAndCaptureRequest();
141
142 // actions are enabled, but replies are not.
143 assertThat(
144 request.getTypeConfig().resolveEntityListModifications(
145 Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
146 ConversationAction.TYPE_OPEN_URL)))
147 .containsExactly(ConversationAction.TYPE_OPEN_URL);
Tony Mak43a899f2018-12-07 18:30:51 +0000148 }
149
150 @Test
Tony Make1a27ac2019-01-31 16:32:19 +0000151 public void testSuggest_settingsOff() {
152 mSettings.mGenerateActions = false;
153 mSettings.mGenerateReplies = false;
154 Notification notification = createMessageNotification();
155 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Mak43a899f2018-12-07 18:30:51 +0000156
Tony Make1a27ac2019-01-31 16:32:19 +0000157 mSmartActionsHelper.suggest(createNotificationEntry());
158
159 verify(mTextClassifier, never())
160 .suggestConversationActions(any(ConversationActions.Request.class));
161 }
162
163 @Test
164 public void testSuggest_settings_repliesOnActionsOff() {
165 mSettings.mGenerateReplies = true;
166 mSettings.mGenerateActions = false;
167 Notification notification = createMessageNotification();
168 when(mStatusBarNotification.getNotification()).thenReturn(notification);
169
170 ConversationActions.Request request = runSuggestAndCaptureRequest();
171
172 // replies are enabled, but actions are not.
173 assertThat(
174 request.getTypeConfig().resolveEntityListModifications(
175 Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
176 ConversationAction.TYPE_OPEN_URL)))
177 .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
178 }
179
180 @Test
181 public void testSuggest_settings_repliesOffActionsOn() {
182 mSettings.mGenerateReplies = false;
183 mSettings.mGenerateActions = true;
184 Notification notification = createMessageNotification();
185 when(mStatusBarNotification.getNotification()).thenReturn(notification);
186
187 ConversationActions.Request request = runSuggestAndCaptureRequest();
188
189 // actions are enabled, but replies are not.
190 assertThat(
191 request.getTypeConfig().resolveEntityListModifications(
192 Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
193 ConversationAction.TYPE_OPEN_URL)))
194 .containsExactly(ConversationAction.TYPE_OPEN_URL);
195 }
196
197
198 @Test
199 public void testSuggest_nonMessageStyleMessageNotification() {
200 Notification notification = createMessageNotification();
201 when(mStatusBarNotification.getNotification()).thenReturn(notification);
202
203 List<ConversationActions.Message> messages =
204 runSuggestAndCaptureRequest().getConversation();
Tony Mak40d5ce02019-02-11 20:38:26 +0000205
Tony Mak43a899f2018-12-07 18:30:51 +0000206 assertThat(messages).hasSize(1);
Tony Make1a27ac2019-01-31 16:32:19 +0000207 MessageSubject.assertThat(messages.get(0)).hasText(MESSAGE);
Tony Mak40d5ce02019-02-11 20:38:26 +0000208 ArgumentCaptor<TextClassifierEvent> argumentCaptor =
209 ArgumentCaptor.forClass(TextClassifierEvent.class);
210 verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
211 TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
212 assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
213 assertThat(textClassifierEvent.getEntityTypes()).asList()
214 .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
Tony Mak43a899f2018-12-07 18:30:51 +0000215 }
216
217 @Test
Tony Make1a27ac2019-01-31 16:32:19 +0000218 public void testSuggest_messageStyle() {
Tony Mak43a899f2018-12-07 18:30:51 +0000219 Person me = new Person.Builder().setName("Me").build();
220 Person userA = new Person.Builder().setName("A").build();
221 Person userB = new Person.Builder().setName("B").build();
222 Notification.MessagingStyle style =
223 new Notification.MessagingStyle(me)
224 .addMessage("firstMessage", 1000, (Person) null)
225 .addMessage("secondMessage", 2000, me)
226 .addMessage("thirdMessage", 3000, userA)
227 .addMessage("fourthMessage", 4000, userB);
228 Notification notification =
229 mNotificationBuilder
230 .setContentText("You have three new messages")
231 .setStyle(style)
Tony Make1a27ac2019-01-31 16:32:19 +0000232 .setActions(createReplyAction())
Tony Mak43a899f2018-12-07 18:30:51 +0000233 .build();
Tony Make1a27ac2019-01-31 16:32:19 +0000234 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Mak43a899f2018-12-07 18:30:51 +0000235
Tony Make1a27ac2019-01-31 16:32:19 +0000236 List<ConversationActions.Message> messages =
237 runSuggestAndCaptureRequest().getConversation();
Tony Mak7ab83082019-02-04 17:43:53 +0000238 assertThat(messages).hasSize(4);
Tony Mak43a899f2018-12-07 18:30:51 +0000239
Tony Mak7ab83082019-02-04 17:43:53 +0000240 ConversationActions.Message firstMessage = messages.get(0);
241 MessageSubject.assertThat(firstMessage).hasText("firstMessage");
242 MessageSubject.assertThat(firstMessage)
243 .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
244 MessageSubject.assertThat(firstMessage)
245 .hasReferenceTime(createZonedDateTimeFromMsUtc(1000));
246
247 ConversationActions.Message secondMessage = messages.get(1);
Tony Mak43a899f2018-12-07 18:30:51 +0000248 MessageSubject.assertThat(secondMessage).hasText("secondMessage");
249 MessageSubject.assertThat(secondMessage)
Tony Mak91daa152019-01-24 16:00:28 +0000250 .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
Tony Mak43a899f2018-12-07 18:30:51 +0000251 MessageSubject.assertThat(secondMessage)
252 .hasReferenceTime(createZonedDateTimeFromMsUtc(2000));
253
Tony Mak7ab83082019-02-04 17:43:53 +0000254 ConversationActions.Message thirdMessage = messages.get(2);
Tony Mak43a899f2018-12-07 18:30:51 +0000255 MessageSubject.assertThat(thirdMessage).hasText("thirdMessage");
256 MessageSubject.assertThat(thirdMessage).hasPerson(userA);
257 MessageSubject.assertThat(thirdMessage)
258 .hasReferenceTime(createZonedDateTimeFromMsUtc(3000));
259
Tony Mak7ab83082019-02-04 17:43:53 +0000260 ConversationActions.Message fourthMessage = messages.get(3);
Tony Mak43a899f2018-12-07 18:30:51 +0000261 MessageSubject.assertThat(fourthMessage).hasText("fourthMessage");
262 MessageSubject.assertThat(fourthMessage).hasPerson(userB);
263 MessageSubject.assertThat(fourthMessage)
264 .hasReferenceTime(createZonedDateTimeFromMsUtc(4000));
Tony Mak40d5ce02019-02-11 20:38:26 +0000265
266 ArgumentCaptor<TextClassifierEvent> argumentCaptor =
267 ArgumentCaptor.forClass(TextClassifierEvent.class);
268 verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
269 TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
270 assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
271 assertThat(textClassifierEvent.getEntityTypes()).asList()
272 .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
Tony Mak43a899f2018-12-07 18:30:51 +0000273 }
274
275 @Test
Tony Mak7ab83082019-02-04 17:43:53 +0000276 public void testSuggest_lastMessageLocalUser() {
277 Person me = new Person.Builder().setName("Me").build();
278 Person userA = new Person.Builder().setName("A").build();
279 Notification.MessagingStyle style =
280 new Notification.MessagingStyle(me)
281 .addMessage("firstMessage", 1000, userA)
282 .addMessage("secondMessage", 2000, me);
283 Notification notification =
284 mNotificationBuilder
285 .setContentText("You have two new messages")
286 .setStyle(style)
287 .setActions(createReplyAction())
288 .build();
289 when(mStatusBarNotification.getNotification()).thenReturn(notification);
290
291 mSmartActionsHelper.suggest(createNotificationEntry());
292
293 verify(mTextClassifier, never())
294 .suggestConversationActions(any(ConversationActions.Request.class));
295 }
296
297 @Test
Tony Make1a27ac2019-01-31 16:32:19 +0000298 public void testSuggest_messageStyle_noPerson() {
Tony Mak43a899f2018-12-07 18:30:51 +0000299 Person me = new Person.Builder().setName("Me").build();
300 Notification.MessagingStyle style =
301 new Notification.MessagingStyle(me).addMessage("message", 1000, (Person) null);
302 Notification notification =
303 mNotificationBuilder
304 .setContentText("You have one new message")
305 .setStyle(style)
Tony Make1a27ac2019-01-31 16:32:19 +0000306 .setActions(createReplyAction())
Tony Mak43a899f2018-12-07 18:30:51 +0000307 .build();
Tony Make1a27ac2019-01-31 16:32:19 +0000308 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Mak43a899f2018-12-07 18:30:51 +0000309
Tony Make1a27ac2019-01-31 16:32:19 +0000310 mSmartActionsHelper.suggest(createNotificationEntry());
Tony Mak43a899f2018-12-07 18:30:51 +0000311
312 verify(mTextClassifier, never())
313 .suggestConversationActions(any(ConversationActions.Request.class));
314 }
315
Tony Make94e0782018-12-14 11:57:54 +0800316 @Test
317 public void testOnSuggestedReplySent() {
Tony Make1a27ac2019-01-31 16:32:19 +0000318 Notification notification = createMessageNotification();
319 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800320
Tony Make1a27ac2019-01-31 16:32:19 +0000321 mSmartActionsHelper.suggest(createNotificationEntry());
Tony Make94e0782018-12-14 11:57:54 +0800322 mSmartActionsHelper.onSuggestedReplySent(
Tony Mak40d5ce02019-02-11 20:38:26 +0000323 NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
Tony Make94e0782018-12-14 11:57:54 +0800324
325 ArgumentCaptor<TextClassifierEvent> argumentCaptor =
326 ArgumentCaptor.forClass(TextClassifierEvent.class);
Tony Mak40d5ce02019-02-11 20:38:26 +0000327 verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
328 List<TextClassifierEvent> events = argumentCaptor.getAllValues();
329 assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
330 assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION);
331 assertThat(events.get(1).getScore()).isEqualTo(SCORE);
Tony Make94e0782018-12-14 11:57:54 +0800332 }
333
334 @Test
335 public void testOnSuggestedReplySent_anotherNotification() {
Tony Make1a27ac2019-01-31 16:32:19 +0000336 Notification notification = createMessageNotification();
337 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800338
Tony Make1a27ac2019-01-31 16:32:19 +0000339 mSmartActionsHelper.suggest(createNotificationEntry());
Tony Make94e0782018-12-14 11:57:54 +0800340 mSmartActionsHelper.onSuggestedReplySent(
Tony Make1a27ac2019-01-31 16:32:19 +0000341 "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
Tony Make94e0782018-12-14 11:57:54 +0800342
Tony Mak40d5ce02019-02-11 20:38:26 +0000343 verify(mTextClassifier, never()).onTextClassifierEvent(
344 argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_SMART_ACTION)));
Tony Make94e0782018-12-14 11:57:54 +0800345 }
346
347 @Test
348 public void testOnSuggestedReplySent_missingResultId() {
349 when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
Tony Mak40d5ce02019-02-11 20:38:26 +0000350 .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null));
Tony Make1a27ac2019-01-31 16:32:19 +0000351 Notification notification = createMessageNotification();
352 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800353
Tony Make1a27ac2019-01-31 16:32:19 +0000354 mSmartActionsHelper.suggest(createNotificationEntry());
Tony Make94e0782018-12-14 11:57:54 +0800355 mSmartActionsHelper.onSuggestedReplySent(
Tony Mak40d5ce02019-02-11 20:38:26 +0000356 NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
Tony Make94e0782018-12-14 11:57:54 +0800357
Tony Mak40d5ce02019-02-11 20:38:26 +0000358 verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class));
Tony Make94e0782018-12-14 11:57:54 +0800359 }
360
361 @Test
362 public void testOnNotificationDirectReply() {
Tony Make1a27ac2019-01-31 16:32:19 +0000363 Notification notification = createMessageNotification();
364 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800365
Tony Make1a27ac2019-01-31 16:32:19 +0000366 mSmartActionsHelper.suggest(createNotificationEntry());
Tony Mak836147e2019-01-08 14:48:31 +0000367 mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY);
Tony Make94e0782018-12-14 11:57:54 +0800368
369 ArgumentCaptor<TextClassifierEvent> argumentCaptor =
370 ArgumentCaptor.forClass(TextClassifierEvent.class);
Tony Mak40d5ce02019-02-11 20:38:26 +0000371 verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
372 List<TextClassifierEvent> events = argumentCaptor.getAllValues();
373 assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
374 assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_MANUAL_REPLY);
Tony Make94e0782018-12-14 11:57:54 +0800375 }
376
377 @Test
378 public void testOnNotificationExpansionChanged() {
Tony Make1a27ac2019-01-31 16:32:19 +0000379 Notification notification = createMessageNotification();
380 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800381
Tony Make1a27ac2019-01-31 16:32:19 +0000382 mSmartActionsHelper.suggest(createNotificationEntry());
383 mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true, true);
Tony Make94e0782018-12-14 11:57:54 +0800384
385 ArgumentCaptor<TextClassifierEvent> argumentCaptor =
386 ArgumentCaptor.forClass(TextClassifierEvent.class);
Tony Mak40d5ce02019-02-11 20:38:26 +0000387 verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
388 List<TextClassifierEvent> events = argumentCaptor.getAllValues();
389 assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
390 assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
Tony Make94e0782018-12-14 11:57:54 +0800391 }
392
393 @Test
394 public void testOnNotificationsSeen_notExpanded() {
Tony Make1a27ac2019-01-31 16:32:19 +0000395 Notification notification = createMessageNotification();
396 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800397
Tony Make1a27ac2019-01-31 16:32:19 +0000398 mSmartActionsHelper.suggest(createNotificationEntry());
399 mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false, false);
Tony Make94e0782018-12-14 11:57:54 +0800400
401 verify(mTextClassifier, never()).onTextClassifierEvent(
Tony Mak40d5ce02019-02-11 20:38:26 +0000402 argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_ACTIONS_SHOWN)));
Tony Make94e0782018-12-14 11:57:54 +0800403 }
404
405 @Test
Tony Mak202f25d2019-01-07 14:40:39 +0000406 public void testOnNotifications_expanded() {
Tony Make1a27ac2019-01-31 16:32:19 +0000407 Notification notification = createMessageNotification();
408 when(mStatusBarNotification.getNotification()).thenReturn(notification);
Tony Make94e0782018-12-14 11:57:54 +0800409
Tony Make1a27ac2019-01-31 16:32:19 +0000410 mSmartActionsHelper.suggest(createNotificationEntry());
411 mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false, true);
Tony Make94e0782018-12-14 11:57:54 +0800412
413 ArgumentCaptor<TextClassifierEvent> argumentCaptor =
414 ArgumentCaptor.forClass(TextClassifierEvent.class);
Tony Mak40d5ce02019-02-11 20:38:26 +0000415 verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
416 List<TextClassifierEvent> events = argumentCaptor.getAllValues();
417 assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
418 assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
Tony Make94e0782018-12-14 11:57:54 +0800419 }
420
Tony Mak43a899f2018-12-07 18:30:51 +0000421 private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
422 return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
423 }
424
Tony Make1a27ac2019-01-31 16:32:19 +0000425 private ConversationActions.Request runSuggestAndCaptureRequest() {
426 mSmartActionsHelper.suggest(createNotificationEntry());
Tony Mak43a899f2018-12-07 18:30:51 +0000427
428 ArgumentCaptor<ConversationActions.Request> argumentCaptor =
429 ArgumentCaptor.forClass(ConversationActions.Request.class);
430 verify(mTextClassifier).suggestConversationActions(argumentCaptor.capture());
Tony Make1a27ac2019-01-31 16:32:19 +0000431 return argumentCaptor.getValue();
432 }
433
434 private Notification.Action createReplyAction() {
435 PendingIntent pendingIntent =
436 PendingIntent.getActivity(mContext, 0, new Intent(mContext, this.getClass()), 0);
437 RemoteInput remoteInput = new RemoteInput.Builder("result")
438 .setAllowFreeFormInput(true)
439 .build();
440 return new Notification.Action.Builder(
441 Icon.createWithResource(mContext.getResources(),
442 android.R.drawable.stat_sys_warning),
443 "Reply", pendingIntent)
444 .addRemoteInput(remoteInput)
445 .build();
446 }
447
448 private NotificationEntry createNotificationEntry() {
449 NotificationChannel channel =
450 new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
451 return new NotificationEntry(mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
452 }
453
454 private Notification createMessageNotification() {
455 return mNotificationBuilder
456 .setContentText(MESSAGE)
457 .setCategory(Notification.CATEGORY_MESSAGE)
458 .setActions(createReplyAction())
459 .build();
Tony Mak43a899f2018-12-07 18:30:51 +0000460 }
461
Tony Make94e0782018-12-14 11:57:54 +0800462 private void assertTextClassifierEvent(
463 TextClassifierEvent textClassifierEvent, int expectedEventType) {
464 assertThat(textClassifierEvent.getEventCategory())
465 .isEqualTo(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS);
466 assertThat(textClassifierEvent.getEventContext().getPackageName())
467 .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName());
468 assertThat(textClassifierEvent.getEventContext().getWidgetType())
469 .isEqualTo(TextClassifier.WIDGET_TYPE_NOTIFICATION);
470 assertThat(textClassifierEvent.getEventType()).isEqualTo(expectedEventType);
471 }
472
Tony Mak43a899f2018-12-07 18:30:51 +0000473 private static final class MessageSubject
474 extends Subject<MessageSubject, ConversationActions.Message> {
475
476 private static final SubjectFactory<MessageSubject, ConversationActions.Message> FACTORY =
477 new SubjectFactory<MessageSubject, ConversationActions.Message>() {
478 @Override
479 public MessageSubject getSubject(
480 @NonNull FailureStrategy failureStrategy,
481 @NonNull ConversationActions.Message subject) {
482 return new MessageSubject(failureStrategy, subject);
483 }
484 };
485
486 private MessageSubject(
487 FailureStrategy failureStrategy, @Nullable ConversationActions.Message subject) {
488 super(failureStrategy, subject);
489 }
490
491 private void hasText(String text) {
492 if (!Objects.equals(text, getSubject().getText().toString())) {
493 failWithBadResults("has text", text, "has", getSubject().getText());
494 }
495 }
496
497 private void hasPerson(Person person) {
498 if (!Objects.equals(person, getSubject().getAuthor())) {
499 failWithBadResults("has author", person, "has", getSubject().getAuthor());
500 }
501 }
502
503 private void hasReferenceTime(ZonedDateTime referenceTime) {
504 if (!Objects.equals(referenceTime, getSubject().getReferenceTime())) {
505 failWithBadResults(
506 "has reference time",
507 referenceTime,
508 "has",
509 getSubject().getReferenceTime());
510 }
511 }
512
513 private static MessageSubject assertThat(ConversationActions.Message message) {
514 return assertAbout(FACTORY).that(message);
515 }
516 }
Tony Mak40d5ce02019-02-11 20:38:26 +0000517
518 private final class TextClassifierEventMatcher implements ArgumentMatcher<TextClassifierEvent> {
519
520 private int mType;
521
522 private TextClassifierEventMatcher(int type) {
523 mType = type;
524 }
525
526 @Override
527 public boolean matches(TextClassifierEvent textClassifierEvent) {
528 if (textClassifierEvent == null) {
529 return false;
530 }
531 return mType == textClassifierEvent.getEventType();
532 }
533 }
Tony Mak43a899f2018-12-07 18:30:51 +0000534}