2 * Copyright (C) 2018 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
15 package com.android.systemui.statusbar.policy;
17 import static android.view.View.MeasureSpec;
19 import static junit.framework.Assert.assertEquals;
20 import static junit.framework.Assert.assertFalse;
21 import static junit.framework.Assert.assertNull;
22 import static junit.framework.Assert.assertTrue;
23 import static junit.framework.Assert.fail;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
32 import android.app.Notification;
33 import android.app.PendingIntent;
34 import android.app.RemoteInput;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.res.Resources;
38 import android.graphics.drawable.Drawable;
39 import android.graphics.drawable.Icon;
40 import android.service.notification.StatusBarNotification;
41 import android.testing.AndroidTestingRunner;
42 import android.testing.TestableLooper;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.widget.Button;
46 import android.widget.LinearLayout;
48 import androidx.test.filters.SmallTest;
50 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
51 import com.android.systemui.R;
52 import com.android.systemui.SysuiTestCase;
53 import com.android.systemui.plugins.ActivityStarter;
54 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
55 import com.android.systemui.statusbar.SmartReplyController;
56 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
57 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
58 import com.android.systemui.statusbar.phone.ShadeController;
60 import org.junit.After;
61 import org.junit.Before;
62 import org.junit.Test;
63 import org.junit.runner.RunWith;
64 import org.mockito.Mock;
65 import org.mockito.MockitoAnnotations;
67 import java.util.ArrayList;
68 import java.util.Collections;
69 import java.util.List;
70 import java.util.concurrent.atomic.AtomicReference;
72 @RunWith(AndroidTestingRunner.class)
73 @TestableLooper.RunWithLooper
75 public class SmartReplyViewTest extends SysuiTestCase {
76 private static final String TEST_RESULT_KEY = "test_result_key";
77 private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
79 private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
80 private static final String TEST_NOTIFICATION_KEY = "akey";
82 private static final String[] TEST_ACTION_TITLES = new String[]{
83 "First action", "Open something", "Action"
86 private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
87 private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
89 private BlockingQueueIntentReceiver mReceiver;
90 private SmartReplyView mView;
91 private View mContainer;
93 private Icon mActionIcon;
95 private int mSingleLinePaddingHorizontal;
96 private int mDoubleLinePaddingHorizontal;
99 @Mock private SmartReplyController mLogger;
100 private NotificationEntry mEntry;
101 private Notification mNotification;
102 @Mock private SmartReplyConstants mConstants;
104 @Mock ActivityStarter mActivityStarter;
105 @Mock HeadsUpManager mHeadsUpManager;
108 public void setUp() {
109 MockitoAnnotations.initMocks(this);
110 mReceiver = new BlockingQueueIntentReceiver();
111 mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
112 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
113 mDependency.injectMockDependency(ShadeController.class);
114 mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
115 mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
117 mContainer = new View(mContext, null);
118 mView = SmartReplyView.inflate(mContext);
120 // Any number of replies are fine.
121 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0);
122 when(mConstants.getMaxSqueezeRemeasureAttempts()).thenReturn(3);
123 when(mConstants.getMaxNumActions()).thenReturn(-1);
124 // Ensure there's no delay before we can click smart suggestions.
125 when(mConstants.getOnClickInitDelay()).thenReturn(0L);
127 final Resources res = mContext.getResources();
128 mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
129 R.dimen.smart_reply_button_padding_horizontal_single_line);
130 mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
131 R.dimen.smart_reply_button_padding_horizontal_double_line);
132 mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
134 mNotification = new Notification.Builder(mContext, "")
135 .setSmallIcon(R.drawable.ic_person)
136 .setContentTitle("Title")
137 .setContentText("Text").build();
138 StatusBarNotification sbn = mock(StatusBarNotification.class);
139 when(sbn.getNotification()).thenReturn(mNotification);
140 when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
141 mEntry = new NotificationEntry(sbn);
143 mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
147 public void tearDown() {
148 mContext.unregisterReceiver(mReceiver);
152 public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
153 setSmartReplies(TEST_CHOICES);
155 mView.getChildAt(2).performClick();
157 Intent resultIntent = mReceiver.waitForIntent();
158 assertEquals(TEST_CHOICES[2],
159 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
160 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
164 public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
165 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
166 setSmartReplies(TEST_CHOICES);
168 mView.getChildAt(2).performClick();
170 assertNull(mReceiver.waitForIntentShortDelay());
174 public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
175 AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
176 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
177 setSmartReplies(TEST_CHOICES);
179 mView.getChildAt(2).performClick();
181 // No intent until the screen is unlocked.
182 assertNull(mReceiver.waitForIntentShortDelay());
184 actionRef.get().onDismiss();
186 // Now the intent should arrive.
187 Intent resultIntent = mReceiver.waitForIntent();
188 assertEquals(TEST_CHOICES[2],
189 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
190 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
194 public void testSendSmartReply_controllerCalled() {
195 setSmartReplies(TEST_CHOICES);
196 mView.getChildAt(2).performClick();
197 verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
198 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
202 public void testSendSmartReply_hidesContainer() {
203 mContainer.setVisibility(View.VISIBLE);
204 setSmartReplies(TEST_CHOICES);
205 mView.getChildAt(0).performClick();
206 assertEquals(View.GONE, mContainer.getVisibility());
210 public void testTapSmartReply_beforeInitDelay_blocked() throws InterruptedException {
211 // 100 seconds is easily enough for our click to always be blocked.
212 when(mConstants.getOnClickInitDelay()).thenReturn(100L * 1000L);
213 setSmartReplies(TEST_CHOICES);
215 mView.getChildAt(2).performClick();
217 assertNull(mReceiver.waitForIntentShortDelay());
221 public void testTapSmartReply_afterInitDelay_clickReceived() throws InterruptedException {
222 final long delayMs = 50L; // Using a small delay to not delay the test suite too much.
223 when(mConstants.getOnClickInitDelay()).thenReturn(delayMs);
224 setSmartReplies(TEST_CHOICES);
226 Thread.sleep(delayMs);
227 mView.getChildAt(2).performClick();
229 // Now the intent should arrive.
230 Intent resultIntent = mReceiver.waitForIntent();
231 assertEquals(TEST_CHOICES[2],
232 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
233 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
237 public void testTapSmartReply_withoutDelayedOnClickListener_bypassesDelay()
238 throws InterruptedException {
239 // 100 seconds is easily enough for our click to always be blocked.
240 when(mConstants.getOnClickInitDelay()).thenReturn(100L * 1000L);
241 setSmartReplies(TEST_CHOICES, false /* useDelayedOnClickListener */);
243 mView.getChildAt(2).performClick();
245 Intent resultIntent = mReceiver.waitForIntent();
246 assertEquals(TEST_CHOICES[2],
247 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
248 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
252 public void testMeasure_empty() {
253 mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
254 assertEquals(500, mView.getMeasuredWidthAndState());
255 assertEquals(0, mView.getMeasuredHeightAndState());
259 public void testLayout_empty() {
260 mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
261 mView.layout(0, 0, 500, 0);
265 // Instead of manually calculating the expected measurement/layout results, we build the
266 // expectations as ordinary linear layouts and then check that the relevant parameters in the
267 // corresponding SmartReplyView and LinearView are equal.
270 public void testMeasure_shortChoices() {
271 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello", "Bye"};
273 // All choices should be displayed as SINGLE-line smart reply buttons.
274 ViewGroup expectedView = buildExpectedView(choices, 1);
275 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
277 setSmartReplies(choices);
278 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
280 assertEqualMeasures(expectedView, mView);
281 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
282 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
283 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
287 public void testLayout_shortChoices() {
288 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello", "Bye"};
290 // All choices should be displayed as SINGLE-line smart reply buttons.
291 ViewGroup expectedView = buildExpectedView(choices, 1);
292 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
293 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
294 10 + expectedView.getMeasuredHeight());
296 setSmartReplies(choices);
297 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
298 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
300 assertEqualLayouts(expectedView, mView);
301 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
302 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
303 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
307 public void testMeasure_choiceWithTwoLines() {
308 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\neveryone", "Bye"};
310 // All choices should be displayed as DOUBLE-line smart reply buttons.
311 ViewGroup expectedView = buildExpectedView(choices, 2);
312 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
314 setSmartReplies(choices);
315 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
317 assertEqualMeasures(expectedView, mView);
318 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
319 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
320 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
324 public void testLayout_choiceWithTwoLines() {
325 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\neveryone", "Bye"};
327 // All choices should be displayed as DOUBLE-line smart reply buttons.
328 ViewGroup expectedView = buildExpectedView(choices, 2);
329 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
330 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
331 10 + expectedView.getMeasuredHeight());
333 setSmartReplies(choices);
334 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
335 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
337 assertEqualLayouts(expectedView, mView);
338 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
339 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
340 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
344 public void testMeasure_choiceWithThreeLines() {
345 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
347 // The choice with three lines should NOT be displayed. All other choices should be
348 // displayed as SINGLE-line smart reply buttons.
349 ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
350 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
352 setSmartReplies(choices);
353 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
355 assertEqualMeasures(expectedView, mView);
356 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
357 assertReplyButtonHidden(mView.getChildAt(1));
358 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
362 public void testLayout_choiceWithThreeLines() {
363 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
365 // The choice with three lines should NOT be displayed. All other choices should be
366 // displayed as SINGLE-line smart reply buttons.
367 ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
368 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
369 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
370 10 + expectedView.getMeasuredHeight());
372 setSmartReplies(choices);
373 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
374 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
376 assertEqualLayouts(expectedView, mView);
377 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
378 // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
379 // testMeasure_choiceWithThreeLines).
380 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
384 public void testMeasure_squeezeLongest() {
385 final CharSequence[] choices = new CharSequence[]{"Short", "Short", "Looooooong replyyyyy"};
387 // All choices should be displayed as DOUBLE-line smart reply buttons.
388 ViewGroup expectedView = buildExpectedView(
389 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
390 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
392 setSmartReplies(choices);
394 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
395 MeasureSpec.UNSPECIFIED);
397 assertEqualMeasures(expectedView, mView);
398 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
399 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
400 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
404 public void testLayout_squeezeLongest() {
405 final CharSequence[] choices = new CharSequence[]{"Short", "Short", "Looooooong replyyyyy"};
407 // All choices should be displayed as DOUBLE-line smart reply buttons.
408 ViewGroup expectedView = buildExpectedView(
409 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
410 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
411 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
412 10 + expectedView.getMeasuredHeight());
414 setSmartReplies(choices);
416 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
417 MeasureSpec.UNSPECIFIED);
418 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
420 assertEqualLayouts(expectedView, mView);
421 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
422 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
423 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
427 public void testMeasure_dropLongest() {
428 final CharSequence[] choices = new CharSequence[]{"Short", "Short",
429 "LooooooongUnbreakableReplyyyyy"};
431 // Short choices should be shown as single line views
432 ViewGroup expectedView = buildExpectedView(
433 new CharSequence[]{"Short", "Short"}, 1);
434 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
435 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
436 10 + expectedView.getMeasuredHeight());
438 setSmartReplies(choices);
440 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
441 MeasureSpec.UNSPECIFIED);
442 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
444 assertEqualLayouts(expectedView, mView);
445 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
446 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
447 assertReplyButtonHidden(mView.getChildAt(2));
450 private void setSmartReplies(CharSequence[] choices) {
451 setSmartReplies(choices, true /* useDelayedOnClickListener */);
454 private void setSmartReplies(CharSequence[] choices, boolean useDelayedOnClickListener) {
455 mView.resetSmartSuggestions(mContainer);
456 List<Button> replyButtons = inflateSmartReplies(choices, false /* fromAssistant */,
457 useDelayedOnClickListener);
458 mView.addPreInflatedButtons(replyButtons);
461 private List<Button> inflateSmartReplies(CharSequence[] choices, boolean fromAssistant,
462 boolean useDelayedOnClickListener) {
463 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
464 new Intent(TEST_ACTION), 0);
465 RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
466 SmartReplyView.SmartReplies smartReplies =
467 new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
468 return mView.inflateRepliesFromRemoteInput(smartReplies, mLogger, mEntry,
469 useDelayedOnClickListener);
472 private Notification.Action createAction(String actionTitle) {
473 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
474 new Intent(TEST_ACTION), 0);
475 return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
478 private List<Notification.Action> createActions(String[] actionTitles) {
479 List<Notification.Action> actions = new ArrayList<>();
480 for (String title : actionTitles) {
481 actions.add(createAction(title));
486 private void setSmartActions(String[] actionTitles) {
487 setSmartActions(actionTitles, true /* useDelayedOnClickListener */);
490 private void setSmartActions(String[] actionTitles, boolean useDelayedOnClickListener) {
491 mView.resetSmartSuggestions(mContainer);
492 List<Button> actions = mView.inflateSmartActions(
493 new SmartReplyView.SmartActions(createActions(actionTitles), false),
497 useDelayedOnClickListener);
498 mView.addPreInflatedButtons(actions);
501 private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
502 setSmartRepliesAndActions(choices, actionTitles, false /* fromAssistant */,
503 true /* useDelayedOnClickListener */);
506 private void setSmartRepliesAndActions(
507 CharSequence[] choices, String[] actionTitles, boolean fromAssistant,
508 boolean useDelayedOnClickListener) {
509 mView.resetSmartSuggestions(mContainer);
510 List<Button> smartSuggestions = inflateSmartReplies(choices, fromAssistant,
511 useDelayedOnClickListener);
512 smartSuggestions.addAll(mView.inflateSmartActions(
513 new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
517 useDelayedOnClickListener));
518 mView.addPreInflatedButtons(smartSuggestions);
521 private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
522 return buildExpectedView(choices, lineCount, new ArrayList<>());
525 /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
526 private ViewGroup buildExpectedView(
527 CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
528 LinearLayout layout = new LinearLayout(mContext);
529 layout.setOrientation(LinearLayout.HORIZONTAL);
531 // Baseline alignment causes expected heights to be off by one or two pixels on some
533 layout.setBaselineAligned(false);
535 final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
536 final int paddingHorizontal;
539 paddingHorizontal = mSingleLinePaddingHorizontal;
542 paddingHorizontal = mDoubleLinePaddingHorizontal;
545 fail("Invalid line count " + lineCount);
550 Button previous = null;
551 SmartReplyView.SmartReplies smartReplies =
552 new SmartReplyView.SmartReplies(choices, null, null, false);
553 for (int i = 0; i < choices.length; ++i) {
554 Button current = SmartReplyView.inflateReplyButton(mView, mContext, i, smartReplies,
555 null /* SmartReplyController */, null /* NotificationEntry */,
556 true /* useDelayedOnClickListener */);
557 current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
558 current.getPaddingBottom());
559 if (previous != null) {
560 ViewGroup.MarginLayoutParams lp =
561 (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
563 lp.leftMargin = mSpacing;
565 lp.rightMargin = mSpacing;
568 layout.addView(current);
573 for (int i = 0; i < actions.size(); ++i) {
574 Button current = inflateActionButton(actions.get(i));
575 current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
576 current.getPaddingBottom());
577 if (previous != null) {
578 ViewGroup.MarginLayoutParams lp =
579 (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
581 lp.leftMargin = mSpacing;
583 lp.rightMargin = mSpacing;
586 layout.addView(current);
593 private static void assertEqualMeasures(View expected, View actual) {
594 assertEquals(expected.getMeasuredWidth(), actual.getMeasuredWidth());
595 assertEquals(expected.getMeasuredHeight(), actual.getMeasuredHeight());
598 private static void assertReplyButtonShownWithEqualMeasures(View expected, View actual) {
599 assertReplyButtonShown(actual);
600 assertEqualMeasures(expected, actual);
601 assertEqualPadding(expected, actual);
604 private static void assertReplyButtonShown(View view) {
605 assertTrue(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
608 private static void assertReplyButtonHidden(View view) {
609 assertFalse(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
612 private static void assertEqualLayouts(View expected, View actual) {
613 assertEquals(expected.getLeft(), actual.getLeft());
614 assertEquals(expected.getTop(), actual.getTop());
615 assertEquals(expected.getRight(), actual.getRight());
616 assertEquals(expected.getBottom(), actual.getBottom());
619 private static void assertEqualPadding(View expected, View actual) {
620 assertEquals(expected.getPaddingLeft(), actual.getPaddingLeft());
621 assertEquals(expected.getPaddingTop(), actual.getPaddingTop());
622 assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
623 assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
627 // =============================================================================================
628 // ============================= Smart Action tests ============================================
629 // =============================================================================================
632 public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
633 setSmartActions(TEST_ACTION_TITLES);
635 mView.getChildAt(2).performClick();
637 verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
642 public void testTapSmartAction_beforeInitDelay_blocked() throws InterruptedException {
643 // 100 seconds is easily enough for our click to always be blocked.
644 when(mConstants.getOnClickInitDelay()).thenReturn(100L * 1000L);
645 setSmartActions(TEST_ACTION_TITLES);
647 mView.getChildAt(2).performClick();
649 verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any());
653 public void testTapSmartAction_afterInitDelay_clickReceived() throws InterruptedException {
654 final long delayMs = 50L; // Using a small delay to not delay the test suite too much.
655 when(mConstants.getOnClickInitDelay()).thenReturn(delayMs);
656 setSmartActions(TEST_ACTION_TITLES);
658 Thread.sleep(delayMs);
659 mView.getChildAt(2).performClick();
661 verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
666 public void testTapSmartAction_withoutDelayedOnClickListener_bypassesDelay() {
667 // 100 seconds is easily enough for our click to always be blocked.
668 when(mConstants.getOnClickInitDelay()).thenReturn(100L * 1000L);
669 setSmartActions(TEST_ACTION_TITLES, false /* useDelayedOnClickListener */);
671 mView.getChildAt(2).performClick();
673 verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
678 public void testMeasure_shortSmartActions() {
679 String[] actions = new String[] {"Hi", "Hello", "Bye"};
680 // All choices should be displayed as SINGLE-line smart action buttons.
681 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
682 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
684 setSmartActions(actions);
685 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
687 assertEqualMeasures(expectedView, mView);
688 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
689 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
690 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
694 public void testLayout_shortSmartActions() {
695 String[] actions = new String[] {"Hi", "Hello", "Bye"};
696 // All choices should be displayed as SINGLE-line smart action buttons.
697 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
698 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
699 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
700 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
701 10 + expectedView.getMeasuredHeight());
703 setSmartActions(actions);
704 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
705 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
707 assertEqualLayouts(expectedView, mView);
708 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
709 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
710 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
714 public void testMeasure_smartActionWithTwoLines() {
715 String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
717 // All actions should be displayed as DOUBLE-line smart action buttons.
718 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
719 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
721 setSmartActions(actions);
722 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
724 assertEqualMeasures(expectedView, mView);
725 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
726 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
727 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
731 public void testLayout_smartActionWithTwoLines() {
732 String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
734 // All actions should be displayed as DOUBLE-line smart action buttons.
735 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
736 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
737 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
738 10 + expectedView.getMeasuredHeight());
740 setSmartActions(actions);
741 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
742 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
744 assertEqualLayouts(expectedView, mView);
745 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
746 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
747 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
751 public void testMeasure_smartActionWithThreeLines() {
752 String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
754 // The action with three lines should NOT be displayed. All other actions should be
755 // displayed as SINGLE-line smart action buttons.
756 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
757 createActions(new String[]{"Hi", "Bye"}));
758 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
760 setSmartActions(actions);
761 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
763 assertEqualMeasures(expectedView, mView);
764 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
765 assertReplyButtonHidden(mView.getChildAt(1));
766 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
770 public void testLayout_smartActionWithThreeLines() {
771 String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
773 // The action with three lines should NOT be displayed. All other actions should be
774 // displayed as SINGLE-line smart action buttons.
775 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
776 createActions(new String[]{"Hi", "Bye"}));
777 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
778 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
779 10 + expectedView.getMeasuredHeight());
781 setSmartActions(actions);
782 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
783 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
785 assertEqualLayouts(expectedView, mView);
786 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
787 // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
788 // testMeasure_smartActionWithThreeLines).
789 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
793 public void testMeasure_squeezeLongestSmartAction() {
794 String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
796 // All actions should be displayed as DOUBLE-line smart action buttons.
797 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
798 createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
799 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
801 setSmartActions(actions);
803 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
804 MeasureSpec.UNSPECIFIED);
806 assertEqualMeasures(expectedView, mView);
807 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
808 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
809 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
813 public void testLayout_squeezeLongestSmartAction() {
814 String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
816 // All actions should be displayed as DOUBLE-line smart action buttons.
817 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
818 createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
819 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
820 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
821 10 + expectedView.getMeasuredHeight());
823 setSmartActions(actions);
825 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
826 MeasureSpec.UNSPECIFIED);
827 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
829 assertEqualLayouts(expectedView, mView);
830 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
831 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
832 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
836 public void testMeasure_dropLongestSmartAction() {
837 String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
839 // Short actions should be shown as single line views
840 ViewGroup expectedView = buildExpectedView(
841 new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
842 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
843 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
844 10 + expectedView.getMeasuredHeight());
846 setSmartActions(actions);
848 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
849 MeasureSpec.UNSPECIFIED);
850 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
852 assertEqualLayouts(expectedView, mView);
853 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
854 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
855 assertReplyButtonHidden(mView.getChildAt(2));
858 private Button inflateActionButton(Notification.Action action) {
859 return SmartReplyView.inflateActionButton(mView, getContext(), 0,
860 new SmartReplyView.SmartActions(Collections.singletonList(action), false),
861 mLogger, mEntry, mHeadsUpManager, true /* useDelayedOnClickListener */);
865 public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
866 // Ensure smart action icons are the same size regardless of the number of text rows in the
868 Button singleLineButton = inflateActionButton(createAction("One line"));
869 Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
870 Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
871 Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
872 assertEquals(singleLineDrawable.getBounds().width(),
873 doubleLineDrawable.getBounds().width());
874 assertEquals(singleLineDrawable.getBounds().height(),
875 doubleLineDrawable.getBounds().height());
879 public void testMeasure_shortChoicesAndActions() {
880 CharSequence[] choices = new String[] {"Hi", "Hello"};
881 String[] actions = new String[] {"Bye"};
882 // All choices should be displayed as SINGLE-line smart action buttons.
883 ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
884 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
886 setSmartRepliesAndActions(choices, actions);
887 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
889 assertEqualMeasures(expectedView, mView);
890 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
891 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
892 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
896 public void testMeasure_choicesAndActionsSqueezeLongestAction() {
897 CharSequence[] choices = new String[] {"Short", "Short"};
898 String[] actions = new String[] {"Looooooong replyyyyy"};
900 // All actions should be displayed as DOUBLE-line smart action buttons.
901 ViewGroup expectedView = buildExpectedView(choices, 2,
902 createActions(new String[] {"Looooooong \nreplyyyyy"}));
903 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
905 setSmartRepliesAndActions(choices, actions);
907 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
908 MeasureSpec.UNSPECIFIED);
910 assertEqualMeasures(expectedView, mView);
911 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
912 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
913 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
917 public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
918 String[] choices = new String[] {"Reply"};
919 String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
921 // All actions should be displayed as DOUBLE-line smart action buttons.
922 ViewGroup expectedView = buildExpectedView(new String[0], 2,
923 createActions(new String[] {
924 "Looooooong \nactioooon", "second \naction", "third \naction"}));
925 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
927 setSmartRepliesAndActions(choices, actions);
929 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
930 MeasureSpec.UNSPECIFIED);
932 assertEqualMeasures(expectedView, mView);
934 assertReplyButtonHidden(mView.getChildAt(0));
936 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
937 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
938 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
942 public void testMeasure_choicesAndActionsPrioritizeActions() {
943 String[] choices = new String[] {"Short", "longer reply"};
944 String[] actions = new String[] {"Looooooong actioooon", "second action"};
946 // All actions should be displayed as DOUBLE-line smart action buttons.
947 ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
948 createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
949 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
951 setSmartRepliesAndActions(choices, actions);
953 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
954 MeasureSpec.UNSPECIFIED);
956 Button firstAction = ((Button) mView.getChildAt(1));
958 assertEqualMeasures(expectedView, mView);
960 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
961 assertReplyButtonHidden(mView.getChildAt(1));
963 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
964 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
968 * Test to ensure that we try to add all possible actions - if we find one action that's too
969 * long we just skip that one rather than quitting altogether.
972 public void testMeasure_skipTooLongActions() {
973 String[] choices = new String[] {};
974 String[] actions = new String[] {
975 "a1", "a2", "this action is soooooooo long it's ridiculous", "a4"};
977 // All actions should be displayed as DOUBLE-line smart action buttons.
978 ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
979 createActions(new String[] {"a1", "a2", "a4"}));
980 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
982 setSmartRepliesAndActions(choices, actions);
984 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
985 MeasureSpec.UNSPECIFIED);
987 assertEqualMeasures(expectedView, mView);
988 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
989 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
990 assertReplyButtonHidden(mView.getChildAt(2));
991 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
995 * Test to ensure that we try to add all possible replies - if we find one reply that's too
996 * long we just skip that one rather than quitting altogether.
999 public void testMeasure_skipTooLongReplies() {
1000 String[] choices = new String[] {
1001 "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
1002 String[] actions = new String[] {};
1004 // All replies should be displayed as single-line smart reply buttons.
1005 ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
1006 1 /* lineCount */, Collections.emptyList());
1007 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1009 setSmartRepliesAndActions(choices, actions);
1011 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1012 MeasureSpec.UNSPECIFIED);
1014 assertEqualMeasures(expectedView, mView);
1015 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
1016 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
1017 assertReplyButtonHidden(mView.getChildAt(2));
1018 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
1022 * Test to ensure that we try to add all possible replies and actions - if we find a reply or
1023 * action that's too long we just skip that one rather than quitting altogether.
1026 public void testMeasure_skipTooLongRepliesAndActions() {
1027 String[] choices = new String[] {
1028 "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
1029 String[] actions = new String[] {
1030 "a1", "ThisActionIsSooooooooLongItsRidiculousIPromise"};
1032 // All replies should be displayed as single-line smart reply buttons.
1033 ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
1034 1 /* lineCount */, createActions(new String[] {"a1"}));
1035 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1037 setSmartRepliesAndActions(choices, actions);
1039 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1040 MeasureSpec.UNSPECIFIED);
1042 assertEqualMeasures(expectedView, mView);
1043 assertReplyButtonShownWithEqualMeasures(
1044 expectedView.getChildAt(0), mView.getChildAt(0)); // r1
1045 assertReplyButtonShownWithEqualMeasures(
1046 expectedView.getChildAt(1), mView.getChildAt(1)); // r2
1047 assertReplyButtonHidden(mView.getChildAt(2)); // long reply
1048 assertReplyButtonShownWithEqualMeasures(
1049 expectedView.getChildAt(2), mView.getChildAt(3)); // r4
1050 assertReplyButtonShownWithEqualMeasures(
1051 expectedView.getChildAt(3), mView.getChildAt(4)); // a1
1052 assertReplyButtonHidden(mView.getChildAt(5)); // long action
1056 public void testMeasure_minNumSystemGeneratedSmartReplies_notEnoughReplies() {
1057 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(3);
1059 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
1060 String[] choices = new String[] {"reply1", "reply2"};
1061 String[] actions = new String[] {"action1"};
1063 ViewGroup expectedView = buildExpectedView(new String[] {}, 1,
1064 createActions(new String[] {"action1"}));
1065 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1067 setSmartRepliesAndActions(
1068 choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
1069 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1071 assertEqualMeasures(expectedView, mView);
1073 assertReplyButtonHidden(mView.getChildAt(0));
1074 assertReplyButtonHidden(mView.getChildAt(1));
1076 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(2));
1080 public void testMeasure_minNumSystemGeneratedSmartReplies_enoughReplies() {
1081 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
1083 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
1084 String[] choices = new String[] {"reply1", "reply2"};
1085 String[] actions = new String[] {"action1"};
1087 ViewGroup expectedView = buildExpectedView(new String[] {"reply1", "reply2"}, 1,
1088 createActions(new String[] {"action1"}));
1089 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1091 setSmartRepliesAndActions(
1092 choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
1093 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1095 assertEqualMeasures(expectedView, mView);
1097 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
1098 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
1100 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
1104 * Ensure actions that are squeezed when shown together with smart replies are unsqueezed if the
1105 * replies are never added (because of the SmartReplyConstants.getMinNumSystemGeneratedReplies()
1109 public void testMeasure_minNumSystemGeneratedSmartReplies_unSqueezeActions() {
1110 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
1112 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
1113 String[] choices = new String[] {"This is a very long two-line reply."};
1114 String[] actions = new String[] {"Short action"};
1116 // The action should be displayed on one line only - since it fits!
1117 ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
1118 createActions(new String[] {"Short action"}));
1119 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1121 setSmartRepliesAndActions(
1122 choices, actions, true /* fromAssistant */, true /* useDelayedOnClickListener */);
1123 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1125 assertEqualMeasures(expectedView, mView);
1127 assertReplyButtonHidden(mView.getChildAt(0));
1129 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
1133 * Test that we don't show more than the maximum number of actions declared in {@link
1134 * SmartReplyConstants}.
1137 public void testMeasure_maxNumActions() {
1138 when(mConstants.getMaxNumActions()).thenReturn(2);
1140 String[] choices = new String[] {};
1141 String[] actions = new String[] {"a1", "a2", "a3", "a4"};
1143 // All replies should be displayed as single-line smart reply buttons.
1144 ViewGroup expectedView = buildExpectedView(new String[] {},
1145 1 /* lineCount */, createActions(new String[] {"a1", "a2"}));
1146 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1148 setSmartRepliesAndActions(choices, actions);
1150 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1151 MeasureSpec.UNSPECIFIED);
1153 assertEqualMeasures(expectedView, mView);
1154 assertReplyButtonShownWithEqualMeasures(
1155 expectedView.getChildAt(0), mView.getChildAt(0)); // a1
1156 assertReplyButtonShownWithEqualMeasures(
1157 expectedView.getChildAt(1), mView.getChildAt(1)); // a2
1158 assertReplyButtonHidden(mView.getChildAt(2)); // a3
1159 assertReplyButtonHidden(mView.getChildAt(3)); // a4
1163 * Test that setting maximum number of actions to -1 means there's no limit to number of actions
1167 public void testMeasure_maxNumActions_noLimit() {
1168 when(mConstants.getMaxNumActions()).thenReturn(-1);
1170 String[] choices = new String[] {};
1171 String[] actions = new String[] {"a1", "a2", "a3", "a4"};
1173 // All replies should be displayed as single-line smart reply buttons.
1174 ViewGroup expectedView = buildExpectedView(new String[] {},
1175 1 /* lineCount */, createActions(new String[] {"a1", "a2", "a3", "a4"}));
1176 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1178 setSmartRepliesAndActions(choices, actions);
1180 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1181 MeasureSpec.UNSPECIFIED);
1183 assertEqualMeasures(expectedView, mView);
1184 assertReplyButtonShownWithEqualMeasures(
1185 expectedView.getChildAt(0), mView.getChildAt(0)); // a1
1186 assertReplyButtonShownWithEqualMeasures(
1187 expectedView.getChildAt(1), mView.getChildAt(1)); // a2
1188 assertReplyButtonShownWithEqualMeasures(
1189 expectedView.getChildAt(2), mView.getChildAt(2)); // a3
1190 assertReplyButtonShownWithEqualMeasures(
1191 expectedView.getChildAt(3), mView.getChildAt(3)); // a4