blob: 6793ecaabdd72f87f7088f054fab1096ef4652de [file] [log] [blame]
Petr Cermak9a3380c2018-01-19 15:00:24 +00001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
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
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
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.
13 */
14
15package com.android.systemui.statusbar.policy;
16
Petr Cermak102431d2018-01-29 10:36:07 +000017import static android.view.View.MeasureSpec;
18
Petr Cermak9a3380c2018-01-19 15:00:24 +000019import static junit.framework.Assert.assertEquals;
Petr Cermak102431d2018-01-29 10:36:07 +000020import static junit.framework.Assert.assertFalse;
Milo Sredkovb0f55e92018-04-04 16:13:28 +010021import static junit.framework.Assert.assertNull;
Petr Cermak102431d2018-01-29 10:36:07 +000022import static junit.framework.Assert.assertTrue;
23import static junit.framework.Assert.fail;
Petr Cermak9a3380c2018-01-19 15:00:24 +000024
Gustav Senntoneab53682018-11-01 16:30:23 +000025import static org.mockito.ArgumentMatchers.any;
Kenny Guy23991102018-04-05 21:18:38 +010026import static org.mockito.Mockito.mock;
Gustav Senntoneab53682018-11-01 16:30:23 +000027import static org.mockito.Mockito.times;
Kenny Guy23991102018-04-05 21:18:38 +010028import static org.mockito.Mockito.verify;
Kenny Guya0f6de82018-04-06 16:20:16 +010029import static org.mockito.Mockito.when;
Kenny Guy23991102018-04-05 21:18:38 +010030
Kenny Guya0f6de82018-04-06 16:20:16 +010031import android.app.Notification;
Petr Cermak9a3380c2018-01-19 15:00:24 +000032import android.app.PendingIntent;
33import android.app.RemoteInput;
34import android.content.Intent;
35import android.content.IntentFilter;
Petr Cermak102431d2018-01-29 10:36:07 +000036import android.content.res.Resources;
Gustav Senntoneab53682018-11-01 16:30:23 +000037import android.graphics.drawable.Drawable;
38import android.graphics.drawable.Icon;
Kenny Guy23991102018-04-05 21:18:38 +010039import android.service.notification.StatusBarNotification;
Petr Cermak9a3380c2018-01-19 15:00:24 +000040import android.support.test.filters.SmallTest;
41import android.testing.AndroidTestingRunner;
42import android.testing.TestableLooper;
Petr Cermak102431d2018-01-29 10:36:07 +000043import android.view.View;
44import android.view.ViewGroup;
45import android.widget.Button;
46import android.widget.LinearLayout;
Petr Cermak9a3380c2018-01-19 15:00:24 +000047
Gustav Sennton13edb492019-01-28 21:40:04 +000048import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Petr Cermak102431d2018-01-29 10:36:07 +000049import com.android.systemui.R;
Petr Cermak9a3380c2018-01-19 15:00:24 +000050import com.android.systemui.SysuiTestCase;
Gustav Senntoneab53682018-11-01 16:30:23 +000051import com.android.systemui.plugins.ActivityStarter;
52import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
Kenny Guya0f6de82018-04-06 16:20:16 +010053import com.android.systemui.statusbar.SmartReplyController;
Ned Burnsf81c4c42019-01-07 14:10:43 -050054import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Milo Sredkovb0f55e92018-04-04 16:13:28 +010055import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
Gustav Senntoneab53682018-11-01 16:30:23 +000056import com.android.systemui.statusbar.phone.ShadeController;
Petr Cermak9a3380c2018-01-19 15:00:24 +000057
Petr Cermak7ef78122018-02-23 15:54:34 +000058import org.junit.After;
Petr Cermak9a3380c2018-01-19 15:00:24 +000059import org.junit.Before;
60import org.junit.Test;
61import org.junit.runner.RunWith;
Kenny Guy23991102018-04-05 21:18:38 +010062import org.mockito.Mock;
63import org.mockito.MockitoAnnotations;
Petr Cermak9a3380c2018-01-19 15:00:24 +000064
Gustav Senntoneab53682018-11-01 16:30:23 +000065import java.util.ArrayList;
Tony Mak7d4b3a52018-11-27 17:29:36 +000066import java.util.Collections;
Gustav Senntoneab53682018-11-01 16:30:23 +000067import java.util.List;
68import java.util.concurrent.atomic.AtomicReference;
69
Petr Cermak9a3380c2018-01-19 15:00:24 +000070@RunWith(AndroidTestingRunner.class)
71@TestableLooper.RunWithLooper
72@SmallTest
73public class SmartReplyViewTest extends SysuiTestCase {
Petr Cermak9a3380c2018-01-19 15:00:24 +000074 private static final String TEST_RESULT_KEY = "test_result_key";
Selim Cinek9a236f72018-02-22 18:35:53 -080075 private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
Petr Cermak102431d2018-01-29 10:36:07 +000076
Petr Cermak9a3380c2018-01-19 15:00:24 +000077 private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
Kenny Guya0f6de82018-04-06 16:20:16 +010078 private static final String TEST_NOTIFICATION_KEY = "akey";
Petr Cermak9a3380c2018-01-19 15:00:24 +000079
Gustav Senntoneab53682018-11-01 16:30:23 +000080 private static final String[] TEST_ACTION_TITLES = new String[]{
81 "First action", "Open something", "Action"
82 };
83
Petr Cermak102431d2018-01-29 10:36:07 +000084 private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
85 private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
86
Petr Cermak9a3380c2018-01-19 15:00:24 +000087 private BlockingQueueIntentReceiver mReceiver;
88 private SmartReplyView mView;
Kenny Guya0f6de82018-04-06 16:20:16 +010089 private View mContainer;
Petr Cermak9a3380c2018-01-19 15:00:24 +000090
Gustav Senntoneab53682018-11-01 16:30:23 +000091 private Icon mActionIcon;
92
Petr Cermak102431d2018-01-29 10:36:07 +000093 private int mSingleLinePaddingHorizontal;
94 private int mDoubleLinePaddingHorizontal;
95 private int mSpacing;
96
Kenny Guya0f6de82018-04-06 16:20:16 +010097 @Mock private SmartReplyController mLogger;
Ned Burnsf81c4c42019-01-07 14:10:43 -050098 private NotificationEntry mEntry;
Kenny Guya0f6de82018-04-06 16:20:16 +010099 private Notification mNotification;
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000100 @Mock private SmartReplyConstants mConstants;
Kenny Guy23991102018-04-05 21:18:38 +0100101
Gustav Senntoneab53682018-11-01 16:30:23 +0000102 @Mock ActivityStarter mActivityStarter;
Gustav Senntond0e84532018-12-03 16:48:36 +0000103 @Mock HeadsUpManager mHeadsUpManager;
Gustav Senntoneab53682018-11-01 16:30:23 +0000104
Petr Cermak9a3380c2018-01-19 15:00:24 +0000105 @Before
106 public void setUp() {
Kenny Guy23991102018-04-05 21:18:38 +0100107 MockitoAnnotations.initMocks(this);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000108 mReceiver = new BlockingQueueIntentReceiver();
109 mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
Milo Sredkove433e9b2018-05-01 22:45:38 +0100110 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
Gustav Senntoneab53682018-11-01 16:30:23 +0000111 mDependency.injectMockDependency(ShadeController.class);
112 mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000113 mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000114
Kenny Guya0f6de82018-04-06 16:20:16 +0100115 mContainer = new View(mContext, null);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000116 mView = SmartReplyView.inflate(mContext, null);
Petr Cermak102431d2018-01-29 10:36:07 +0000117
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000118 // Any number of replies are fine.
119 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0);
120 when(mConstants.getMaxSqueezeRemeasureAttempts()).thenReturn(3);
Gustav Sennton4bf5ff52019-01-16 14:27:25 +0000121 when(mConstants.getMaxNumActions()).thenReturn(-1);
Petr Cermak102431d2018-01-29 10:36:07 +0000122
123 final Resources res = mContext.getResources();
124 mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
125 R.dimen.smart_reply_button_padding_horizontal_single_line);
126 mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
127 R.dimen.smart_reply_button_padding_horizontal_double_line);
128 mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
Kenny Guy23991102018-04-05 21:18:38 +0100129
Kenny Guya0f6de82018-04-06 16:20:16 +0100130 mNotification = new Notification.Builder(mContext, "")
131 .setSmallIcon(R.drawable.ic_person)
132 .setContentTitle("Title")
133 .setContentText("Text").build();
134 StatusBarNotification sbn = mock(StatusBarNotification.class);
135 when(sbn.getNotification()).thenReturn(mNotification);
136 when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500137 mEntry = new NotificationEntry(sbn);
Gustav Senntoneab53682018-11-01 16:30:23 +0000138
139 mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000140 }
141
Petr Cermak7ef78122018-02-23 15:54:34 +0000142 @After
143 public void tearDown() {
144 mContext.unregisterReceiver(mReceiver);
145 }
146
Petr Cermak9a3380c2018-01-19 15:00:24 +0000147 @Test
148 public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
Gustav Senntoneab53682018-11-01 16:30:23 +0000149 setSmartReplies(TEST_CHOICES);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000150
151 mView.getChildAt(2).performClick();
152
153 Intent resultIntent = mReceiver.waitForIntent();
154 assertEquals(TEST_CHOICES[2],
155 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
156 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
157 }
Petr Cermak102431d2018-01-29 10:36:07 +0000158
159 @Test
Milo Sredkovb0f55e92018-04-04 16:13:28 +0100160 public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
Milo Sredkove433e9b2018-05-01 22:45:38 +0100161 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
Gustav Senntoneab53682018-11-01 16:30:23 +0000162 setSmartReplies(TEST_CHOICES);
Milo Sredkovb0f55e92018-04-04 16:13:28 +0100163
164 mView.getChildAt(2).performClick();
165
166 assertNull(mReceiver.waitForIntent());
167 }
168
169 @Test
170 public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
171 AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
Milo Sredkove433e9b2018-05-01 22:45:38 +0100172 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
Gustav Senntoneab53682018-11-01 16:30:23 +0000173 setSmartReplies(TEST_CHOICES);
Milo Sredkovb0f55e92018-04-04 16:13:28 +0100174
175 mView.getChildAt(2).performClick();
176
177 // No intent until the screen is unlocked.
178 assertNull(mReceiver.waitForIntent());
179
180 actionRef.get().onDismiss();
181
182 // Now the intent should arrive.
183 Intent resultIntent = mReceiver.waitForIntent();
184 assertEquals(TEST_CHOICES[2],
185 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
186 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
187 }
188
189 @Test
Kenny Guya0f6de82018-04-06 16:20:16 +0100190 public void testSendSmartReply_controllerCalled() {
Gustav Senntoneab53682018-11-01 16:30:23 +0000191 setSmartReplies(TEST_CHOICES);
Kenny Guy23991102018-04-05 21:18:38 +0100192 mView.getChildAt(2).performClick();
Tony Mak29996702018-11-26 16:23:34 +0000193 verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
Milo Sredkov13d88112019-02-01 12:23:24 +0000194 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
Kenny Guya0f6de82018-04-06 16:20:16 +0100195 }
196
197 @Test
198 public void testSendSmartReply_hidesContainer() {
199 mContainer.setVisibility(View.VISIBLE);
Gustav Senntoneab53682018-11-01 16:30:23 +0000200 setSmartReplies(TEST_CHOICES);
Kenny Guya0f6de82018-04-06 16:20:16 +0100201 mView.getChildAt(0).performClick();
202 assertEquals(View.GONE, mContainer.getVisibility());
Kenny Guy23991102018-04-05 21:18:38 +0100203 }
204
205 @Test
Petr Cermak102431d2018-01-29 10:36:07 +0000206 public void testMeasure_empty() {
207 mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
208 assertEquals(500, mView.getMeasuredWidthAndState());
209 assertEquals(0, mView.getMeasuredHeightAndState());
210 }
211
212 @Test
213 public void testLayout_empty() {
214 mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
215 mView.layout(0, 0, 500, 0);
216 }
217
218
219 // Instead of manually calculating the expected measurement/layout results, we build the
220 // expectations as ordinary linear layouts and then check that the relevant parameters in the
221 // corresponding SmartReplyView and LinearView are equal.
222
223 @Test
224 public void testMeasure_shortChoices() {
225 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello", "Bye"};
226
227 // All choices should be displayed as SINGLE-line smart reply buttons.
228 ViewGroup expectedView = buildExpectedView(choices, 1);
229 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
230
Gustav Senntoneab53682018-11-01 16:30:23 +0000231 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000232 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
233
234 assertEqualMeasures(expectedView, mView);
235 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
236 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
237 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
238 }
239
240 @Test
241 public void testLayout_shortChoices() {
242 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello", "Bye"};
243
244 // All choices should be displayed as SINGLE-line smart reply buttons.
245 ViewGroup expectedView = buildExpectedView(choices, 1);
246 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
247 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
248 10 + expectedView.getMeasuredHeight());
249
Gustav Senntoneab53682018-11-01 16:30:23 +0000250 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000251 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
252 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
253
254 assertEqualLayouts(expectedView, mView);
255 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
256 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
257 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
258 }
259
260 @Test
261 public void testMeasure_choiceWithTwoLines() {
262 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\neveryone", "Bye"};
263
264 // All choices should be displayed as DOUBLE-line smart reply buttons.
265 ViewGroup expectedView = buildExpectedView(choices, 2);
266 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
267
Gustav Senntoneab53682018-11-01 16:30:23 +0000268 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000269 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
270
271 assertEqualMeasures(expectedView, mView);
272 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
273 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
274 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
275 }
276
277 @Test
278 public void testLayout_choiceWithTwoLines() {
279 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\neveryone", "Bye"};
280
281 // All choices should be displayed as DOUBLE-line smart reply buttons.
282 ViewGroup expectedView = buildExpectedView(choices, 2);
283 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
284 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
285 10 + expectedView.getMeasuredHeight());
286
Gustav Senntoneab53682018-11-01 16:30:23 +0000287 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000288 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
289 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
290
291 assertEqualLayouts(expectedView, mView);
292 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
293 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
294 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
295 }
296
297 @Test
298 public void testMeasure_choiceWithThreeLines() {
299 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
300
301 // The choice with three lines should NOT be displayed. All other choices should be
302 // displayed as SINGLE-line smart reply buttons.
303 ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
304 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
305
Gustav Senntoneab53682018-11-01 16:30:23 +0000306 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000307 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
308
309 assertEqualMeasures(expectedView, mView);
310 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
311 assertReplyButtonHidden(mView.getChildAt(1));
312 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
313 }
314
315 @Test
316 public void testLayout_choiceWithThreeLines() {
317 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
318
319 // The choice with three lines should NOT be displayed. All other choices should be
320 // displayed as SINGLE-line smart reply buttons.
321 ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
322 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
323 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
324 10 + expectedView.getMeasuredHeight());
325
Gustav Senntoneab53682018-11-01 16:30:23 +0000326 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000327 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
328 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
329
330 assertEqualLayouts(expectedView, mView);
331 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
332 // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
333 // testMeasure_choiceWithThreeLines).
334 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
335 }
336
337 @Test
338 public void testMeasure_squeezeLongest() {
339 final CharSequence[] choices = new CharSequence[]{"Short", "Short", "Looooooong replyyyyy"};
340
341 // All choices should be displayed as DOUBLE-line smart reply buttons.
342 ViewGroup expectedView = buildExpectedView(
343 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
344 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
345
Gustav Senntoneab53682018-11-01 16:30:23 +0000346 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000347 mView.measure(
348 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
349 MeasureSpec.UNSPECIFIED);
350
351 assertEqualMeasures(expectedView, mView);
352 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
353 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
354 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
355 }
356
357 @Test
358 public void testLayout_squeezeLongest() {
359 final CharSequence[] choices = new CharSequence[]{"Short", "Short", "Looooooong replyyyyy"};
360
361 // All choices should be displayed as DOUBLE-line smart reply buttons.
362 ViewGroup expectedView = buildExpectedView(
363 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
364 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
365 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
366 10 + expectedView.getMeasuredHeight());
367
Gustav Senntoneab53682018-11-01 16:30:23 +0000368 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000369 mView.measure(
370 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
371 MeasureSpec.UNSPECIFIED);
372 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
373
374 assertEqualLayouts(expectedView, mView);
375 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
376 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
377 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
378 }
379
Kenny Guy48ee6d62018-05-09 16:51:26 +0100380 @Test
381 public void testMeasure_dropLongest() {
382 final CharSequence[] choices = new CharSequence[]{"Short", "Short",
383 "LooooooongUnbreakableReplyyyyy"};
384
385 // Short choices should be shown as single line views
386 ViewGroup expectedView = buildExpectedView(
387 new CharSequence[]{"Short", "Short"}, 1);
388 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
389 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
390 10 + expectedView.getMeasuredHeight());
391
Gustav Senntoneab53682018-11-01 16:30:23 +0000392 setSmartReplies(choices);
Kenny Guy48ee6d62018-05-09 16:51:26 +0100393 mView.measure(
394 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
395 MeasureSpec.UNSPECIFIED);
396 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
397
398 assertEqualLayouts(expectedView, mView);
399 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
400 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
401 assertReplyButtonHidden(mView.getChildAt(2));
402 }
403
Gustav Senntoneab53682018-11-01 16:30:23 +0000404 private void setSmartReplies(CharSequence[] choices) {
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000405 setSmartReplies(choices, false /* fromAssistant */);
Tony Mak29996702018-11-26 16:23:34 +0000406 }
407
408 private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
Petr Cermak102431d2018-01-29 10:36:07 +0000409 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
410 new Intent(TEST_ACTION), 0);
411 RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
Tony Mak29996702018-11-26 16:23:34 +0000412 SmartReplyView.SmartReplies smartReplies =
413 new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
Gustav Senntoneab53682018-11-01 16:30:23 +0000414 mView.resetSmartSuggestions(mContainer);
Tony Mak29996702018-11-26 16:23:34 +0000415 mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
Gustav Senntoneab53682018-11-01 16:30:23 +0000416 }
417
418 private Notification.Action createAction(String actionTitle) {
419 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
420 new Intent(TEST_ACTION), 0);
421 return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
422 }
423
424 private List<Notification.Action> createActions(String[] actionTitles) {
425 List<Notification.Action> actions = new ArrayList<>();
426 for (String title : actionTitles) {
427 actions.add(createAction(title));
428 }
429 return actions;
430 }
431
432 private void setSmartActions(String[] actionTitles) {
433 mView.resetSmartSuggestions(mContainer);
Tony Mak7d4b3a52018-11-27 17:29:36 +0000434 mView.addSmartActions(
435 new SmartReplyView.SmartActions(createActions(actionTitles), false),
436 mLogger,
Gustav Senntond0e84532018-12-03 16:48:36 +0000437 mEntry,
438 mHeadsUpManager);
Gustav Senntoneab53682018-11-01 16:30:23 +0000439 }
440
441 private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000442 setSmartRepliesAndActions(choices, actionTitles, false /* fromAssistant */);
443 }
444
445 private void setSmartRepliesAndActions(
446 CharSequence[] choices, String[] actionTitles, boolean fromAssistant) {
447 setSmartReplies(choices, fromAssistant);
Tony Mak7d4b3a52018-11-27 17:29:36 +0000448 mView.addSmartActions(
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000449 new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
Tony Mak7d4b3a52018-11-27 17:29:36 +0000450 mLogger,
Gustav Senntond0e84532018-12-03 16:48:36 +0000451 mEntry,
452 mHeadsUpManager);
Gustav Senntoneab53682018-11-01 16:30:23 +0000453 }
454
455 private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
456 return buildExpectedView(choices, lineCount, new ArrayList<>());
Petr Cermak102431d2018-01-29 10:36:07 +0000457 }
458
459 /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
Gustav Senntoneab53682018-11-01 16:30:23 +0000460 private ViewGroup buildExpectedView(
461 CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
Petr Cermak102431d2018-01-29 10:36:07 +0000462 LinearLayout layout = new LinearLayout(mContext);
463 layout.setOrientation(LinearLayout.HORIZONTAL);
464
465 // Baseline alignment causes expected heights to be off by one or two pixels on some
466 // devices.
467 layout.setBaselineAligned(false);
468
469 final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
470 final int paddingHorizontal;
471 switch (lineCount) {
472 case 1:
473 paddingHorizontal = mSingleLinePaddingHorizontal;
474 break;
475 case 2:
476 paddingHorizontal = mDoubleLinePaddingHorizontal;
477 break;
478 default:
479 fail("Invalid line count " + lineCount);
480 return null;
481 }
482
Gustav Senntoneab53682018-11-01 16:30:23 +0000483 // Add smart replies
Petr Cermak102431d2018-01-29 10:36:07 +0000484 Button previous = null;
Tony Mak29996702018-11-26 16:23:34 +0000485 SmartReplyView.SmartReplies smartReplies =
486 new SmartReplyView.SmartReplies(choices, null, null, false);
Kenny Guy23991102018-04-05 21:18:38 +0100487 for (int i = 0; i < choices.length; ++i) {
Tony Mak29996702018-11-26 16:23:34 +0000488 Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies,
489 null, null);
Petr Cermak102431d2018-01-29 10:36:07 +0000490 current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
491 current.getPaddingBottom());
492 if (previous != null) {
493 ViewGroup.MarginLayoutParams lp =
494 (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
495 if (isRtl) {
496 lp.leftMargin = mSpacing;
497 } else {
498 lp.rightMargin = mSpacing;
499 }
500 }
501 layout.addView(current);
502 previous = current;
503 }
504
Gustav Senntoneab53682018-11-01 16:30:23 +0000505 // Add smart actions
506 for (int i = 0; i < actions.size(); ++i) {
507 Button current = inflateActionButton(actions.get(i));
508 current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
509 current.getPaddingBottom());
510 if (previous != null) {
511 ViewGroup.MarginLayoutParams lp =
512 (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
513 if (isRtl) {
514 lp.leftMargin = mSpacing;
515 } else {
516 lp.rightMargin = mSpacing;
517 }
518 }
519 layout.addView(current);
520 previous = current;
521 }
522
Petr Cermak102431d2018-01-29 10:36:07 +0000523 return layout;
524 }
525
526 private static void assertEqualMeasures(View expected, View actual) {
527 assertEquals(expected.getMeasuredWidth(), actual.getMeasuredWidth());
528 assertEquals(expected.getMeasuredHeight(), actual.getMeasuredHeight());
529 }
530
531 private static void assertReplyButtonShownWithEqualMeasures(View expected, View actual) {
532 assertReplyButtonShown(actual);
533 assertEqualMeasures(expected, actual);
Kenny Guy48ee6d62018-05-09 16:51:26 +0100534 assertEqualPadding(expected, actual);
Petr Cermak102431d2018-01-29 10:36:07 +0000535 }
536
537 private static void assertReplyButtonShown(View view) {
538 assertTrue(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
539 }
540
541 private static void assertReplyButtonHidden(View view) {
542 assertFalse(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
543 }
544
545 private static void assertEqualLayouts(View expected, View actual) {
546 assertEquals(expected.getLeft(), actual.getLeft());
547 assertEquals(expected.getTop(), actual.getTop());
548 assertEquals(expected.getRight(), actual.getRight());
549 assertEquals(expected.getBottom(), actual.getBottom());
550 }
Kenny Guy48ee6d62018-05-09 16:51:26 +0100551
552 private static void assertEqualPadding(View expected, View actual) {
553 assertEquals(expected.getPaddingLeft(), actual.getPaddingLeft());
554 assertEquals(expected.getPaddingTop(), actual.getPaddingTop());
555 assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
556 assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
557 }
Gustav Senntoneab53682018-11-01 16:30:23 +0000558
559
560 // =============================================================================================
561 // ============================= Smart Action tests ============================================
562 // =============================================================================================
563
564 @Test
565 public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
566 setSmartActions(TEST_ACTION_TITLES);
567
568 mView.getChildAt(2).performClick();
569
Tony Mak7d4b3a52018-11-27 17:29:36 +0000570 verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
Gustav Senntoneab53682018-11-01 16:30:23 +0000571 }
572
573 @Test
574 public void testMeasure_shortSmartActions() {
575 String[] actions = new String[] {"Hi", "Hello", "Bye"};
576 // All choices should be displayed as SINGLE-line smart action buttons.
577 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
578 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
579
580 setSmartActions(actions);
581 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
582
583 assertEqualMeasures(expectedView, mView);
584 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
585 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
586 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
587 }
588
589 @Test
590 public void testLayout_shortSmartActions() {
591 String[] actions = new String[] {"Hi", "Hello", "Bye"};
592 // All choices should be displayed as SINGLE-line smart action buttons.
593 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
594 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
595 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
596 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
597 10 + expectedView.getMeasuredHeight());
598
599 setSmartActions(actions);
600 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
601 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
602
603 assertEqualLayouts(expectedView, mView);
604 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
605 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
606 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
607 }
608
609 @Test
610 public void testMeasure_smartActionWithTwoLines() {
611 String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
612
613 // All actions should be displayed as DOUBLE-line smart action buttons.
614 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
615 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
616
617 setSmartActions(actions);
618 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
619
620 assertEqualMeasures(expectedView, mView);
621 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
622 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
623 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
624 }
625
626 @Test
627 public void testLayout_smartActionWithTwoLines() {
628 String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
629
630 // All actions should be displayed as DOUBLE-line smart action buttons.
631 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
632 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
633 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
634 10 + expectedView.getMeasuredHeight());
635
636 setSmartActions(actions);
637 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
638 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
639
640 assertEqualLayouts(expectedView, mView);
641 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
642 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
643 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
644 }
645
646 @Test
647 public void testMeasure_smartActionWithThreeLines() {
648 String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
649
650 // The action with three lines should NOT be displayed. All other actions should be
651 // displayed as SINGLE-line smart action buttons.
652 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
653 createActions(new String[]{"Hi", "Bye"}));
654 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
655
656 setSmartActions(actions);
657 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
658
659 assertEqualMeasures(expectedView, mView);
660 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
661 assertReplyButtonHidden(mView.getChildAt(1));
662 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
663 }
664
665 @Test
666 public void testLayout_smartActionWithThreeLines() {
667 String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
668
669 // The action with three lines should NOT be displayed. All other actions should be
670 // displayed as SINGLE-line smart action buttons.
671 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
672 createActions(new String[]{"Hi", "Bye"}));
673 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
674 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
675 10 + expectedView.getMeasuredHeight());
676
677 setSmartActions(actions);
678 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
679 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
680
681 assertEqualLayouts(expectedView, mView);
682 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
683 // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
684 // testMeasure_smartActionWithThreeLines).
685 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
686 }
687
688 @Test
689 public void testMeasure_squeezeLongestSmartAction() {
690 String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
691
692 // All actions should be displayed as DOUBLE-line smart action buttons.
693 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
694 createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
695 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
696
697 setSmartActions(actions);
698 mView.measure(
699 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
700 MeasureSpec.UNSPECIFIED);
701
702 assertEqualMeasures(expectedView, mView);
703 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
704 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
705 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
706 }
707
708 @Test
709 public void testLayout_squeezeLongestSmartAction() {
710 String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
711
712 // All actions should be displayed as DOUBLE-line smart action buttons.
713 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
714 createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
715 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
716 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
717 10 + expectedView.getMeasuredHeight());
718
719 setSmartActions(actions);
720 mView.measure(
721 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
722 MeasureSpec.UNSPECIFIED);
723 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
724
725 assertEqualLayouts(expectedView, mView);
726 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
727 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
728 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
729 }
730
731 @Test
732 public void testMeasure_dropLongestSmartAction() {
733 String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
734
735 // Short actions should be shown as single line views
736 ViewGroup expectedView = buildExpectedView(
737 new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
738 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
739 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
740 10 + expectedView.getMeasuredHeight());
741
742 setSmartActions(actions);
743 mView.measure(
744 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
745 MeasureSpec.UNSPECIFIED);
746 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
747
748 assertEqualLayouts(expectedView, mView);
749 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
750 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
751 assertReplyButtonHidden(mView.getChildAt(2));
752 }
753
754 private Button inflateActionButton(Notification.Action action) {
Tony Mak7d4b3a52018-11-27 17:29:36 +0000755 return mView.inflateActionButton(getContext(), mView, 0,
756 new SmartReplyView.SmartActions(Collections.singletonList(action), false),
Gustav Senntond0e84532018-12-03 16:48:36 +0000757 mLogger, mEntry, mHeadsUpManager);
Gustav Senntoneab53682018-11-01 16:30:23 +0000758 }
759
760 @Test
761 public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
762 // Ensure smart action icons are the same size regardless of the number of text rows in the
763 // button.
764 Button singleLineButton = inflateActionButton(createAction("One line"));
765 Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
766 Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
767 Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
768 assertEquals(singleLineDrawable.getBounds().width(),
769 doubleLineDrawable.getBounds().width());
770 assertEquals(singleLineDrawable.getBounds().height(),
771 doubleLineDrawable.getBounds().height());
772 }
773
774 @Test
775 public void testMeasure_shortChoicesAndActions() {
776 CharSequence[] choices = new String[] {"Hi", "Hello"};
777 String[] actions = new String[] {"Bye"};
778 // All choices should be displayed as SINGLE-line smart action buttons.
779 ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
780 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
781
782 setSmartRepliesAndActions(choices, actions);
783 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
784
785 assertEqualMeasures(expectedView, mView);
786 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
787 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
788 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
789 }
790
791 @Test
792 public void testMeasure_choicesAndActionsSqueezeLongestAction() {
793 CharSequence[] choices = new String[] {"Short", "Short"};
794 String[] actions = new String[] {"Looooooong replyyyyy"};
795
796 // All actions should be displayed as DOUBLE-line smart action buttons.
797 ViewGroup expectedView = buildExpectedView(choices, 2,
798 createActions(new String[] {"Looooooong \nreplyyyyy"}));
799 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
800
801 setSmartRepliesAndActions(choices, actions);
802 mView.measure(
803 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
804 MeasureSpec.UNSPECIFIED);
805
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));
810 }
Gustav Senntonb149a1a2018-11-20 17:25:50 +0000811
812 @Test
813 public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
814 String[] choices = new String[] {"Reply"};
815 String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
816
817 // All actions should be displayed as DOUBLE-line smart action buttons.
818 ViewGroup expectedView = buildExpectedView(new String[0], 2,
819 createActions(new String[] {
820 "Looooooong \nactioooon", "second \naction", "third \naction"}));
821 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
822
823 setSmartRepliesAndActions(choices, actions);
824 mView.measure(
825 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
826 MeasureSpec.UNSPECIFIED);
827
828 assertEqualMeasures(expectedView, mView);
829 // smart replies
830 assertReplyButtonHidden(mView.getChildAt(0));
831 // smart actions
832 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
833 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
834 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
835 }
836
837 @Test
838 public void testMeasure_choicesAndActionsPrioritizeActions() {
839 String[] choices = new String[] {"Short", "longer reply"};
840 String[] actions = new String[] {"Looooooong actioooon", "second action"};
841
842 // All actions should be displayed as DOUBLE-line smart action buttons.
843 ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
844 createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
845 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
846
847 setSmartRepliesAndActions(choices, actions);
848 mView.measure(
849 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
850 MeasureSpec.UNSPECIFIED);
851
852 Button firstAction = ((Button) mView.getChildAt(1));
853
854 assertEqualMeasures(expectedView, mView);
855 // smart replies
856 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
857 assertReplyButtonHidden(mView.getChildAt(1));
858 // smart actions
859 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
860 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
861 }
Gustav Sennton35156752018-12-20 10:35:03 +0000862
863 /**
864 * Test to ensure that we try to add all possible actions - if we find one action that's too
865 * long we just skip that one rather than quitting altogether.
866 */
867 @Test
868 public void testMeasure_skipTooLongActions() {
869 String[] choices = new String[] {};
870 String[] actions = new String[] {
871 "a1", "a2", "this action is soooooooo long it's ridiculous", "a4"};
872
873 // All actions should be displayed as DOUBLE-line smart action buttons.
874 ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
875 createActions(new String[] {"a1", "a2", "a4"}));
876 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
877
878 setSmartRepliesAndActions(choices, actions);
879 mView.measure(
880 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
881 MeasureSpec.UNSPECIFIED);
882
883 assertEqualMeasures(expectedView, mView);
884 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
885 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
886 assertReplyButtonHidden(mView.getChildAt(2));
887 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
888 }
889
890 /**
891 * Test to ensure that we try to add all possible replies - if we find one reply that's too
892 * long we just skip that one rather than quitting altogether.
893 */
894 @Test
895 public void testMeasure_skipTooLongReplies() {
896 String[] choices = new String[] {
897 "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
898 String[] actions = new String[] {};
899
900 // All replies should be displayed as single-line smart reply buttons.
901 ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
902 1 /* lineCount */, Collections.emptyList());
903 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
904
905 setSmartRepliesAndActions(choices, actions);
906 mView.measure(
907 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
908 MeasureSpec.UNSPECIFIED);
909
910 assertEqualMeasures(expectedView, mView);
911 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
912 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
913 assertReplyButtonHidden(mView.getChildAt(2));
914 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
915 }
916
917 /**
918 * Test to ensure that we try to add all possible replies and actions - if we find a reply or
919 * action that's too long we just skip that one rather than quitting altogether.
920 */
921 @Test
922 public void testMeasure_skipTooLongRepliesAndActions() {
923 String[] choices = new String[] {
924 "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
925 String[] actions = new String[] {
926 "a1", "ThisActionIsSooooooooLongItsRidiculousIPromise"};
927
928 // All replies should be displayed as single-line smart reply buttons.
929 ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
930 1 /* lineCount */, createActions(new String[] {"a1"}));
931 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
932
933 setSmartRepliesAndActions(choices, actions);
934 mView.measure(
935 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
936 MeasureSpec.UNSPECIFIED);
937
938 assertEqualMeasures(expectedView, mView);
939 assertReplyButtonShownWithEqualMeasures(
940 expectedView.getChildAt(0), mView.getChildAt(0)); // r1
941 assertReplyButtonShownWithEqualMeasures(
942 expectedView.getChildAt(1), mView.getChildAt(1)); // r2
943 assertReplyButtonHidden(mView.getChildAt(2)); // long reply
944 assertReplyButtonShownWithEqualMeasures(
945 expectedView.getChildAt(2), mView.getChildAt(3)); // r4
946 assertReplyButtonShownWithEqualMeasures(
947 expectedView.getChildAt(3), mView.getChildAt(4)); // a1
948 assertReplyButtonHidden(mView.getChildAt(5)); // long action
949 }
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000950
951 @Test
952 public void testMeasure_minNumSystemGeneratedSmartReplies_notEnoughReplies() {
953 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(3);
954
955 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
956 String[] choices = new String[] {"reply1", "reply2"};
957 String[] actions = new String[] {"action1"};
958
959 ViewGroup expectedView = buildExpectedView(new String[] {}, 1,
960 createActions(new String[] {"action1"}));
961 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
962
963 setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
964 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
965
966 assertEqualMeasures(expectedView, mView);
967 // smart replies
968 assertReplyButtonHidden(mView.getChildAt(0));
969 assertReplyButtonHidden(mView.getChildAt(1));
970 // smart actions
971 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(2));
972 }
973
974 @Test
975 public void testMeasure_minNumSystemGeneratedSmartReplies_enoughReplies() {
976 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
977
978 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
979 String[] choices = new String[] {"reply1", "reply2"};
980 String[] actions = new String[] {"action1"};
981
982 ViewGroup expectedView = buildExpectedView(new String[] {"reply1", "reply2"}, 1,
983 createActions(new String[] {"action1"}));
984 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
985
986 setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
987 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
988
989 assertEqualMeasures(expectedView, mView);
990 // smart replies
991 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
992 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
993 // smart actions
994 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
995 }
996
997 /**
998 * Ensure actions that are squeezed when shown together with smart replies are unsqueezed if the
999 * replies are never added (because of the SmartReplyConstants.getMinNumSystemGeneratedReplies()
1000 * flag).
1001 */
1002 @Test
1003 public void testMeasure_minNumSystemGeneratedSmartReplies_unSqueezeActions() {
1004 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
1005
1006 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
1007 String[] choices = new String[] {"This is a very long two-line reply."};
1008 String[] actions = new String[] {"Short action"};
1009
1010 // The action should be displayed on one line only - since it fits!
1011 ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
1012 createActions(new String[] {"Short action"}));
1013 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1014
1015 setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
1016 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1017
1018 assertEqualMeasures(expectedView, mView);
1019 // smart replies
1020 assertReplyButtonHidden(mView.getChildAt(0));
1021 // smart actions
1022 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
1023 }
Gustav Sennton4bf5ff52019-01-16 14:27:25 +00001024
1025 /**
1026 * Test that we don't show more than the maximum number of actions declared in {@link
1027 * SmartReplyConstants}.
1028 */
1029 @Test
1030 public void testMeasure_maxNumActions() {
1031 when(mConstants.getMaxNumActions()).thenReturn(2);
1032
1033 String[] choices = new String[] {};
1034 String[] actions = new String[] {"a1", "a2", "a3", "a4"};
1035
1036 // All replies should be displayed as single-line smart reply buttons.
1037 ViewGroup expectedView = buildExpectedView(new String[] {},
1038 1 /* lineCount */, createActions(new String[] {"a1", "a2"}));
1039 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1040
1041 setSmartRepliesAndActions(choices, actions);
1042 mView.measure(
1043 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1044 MeasureSpec.UNSPECIFIED);
1045
1046 assertEqualMeasures(expectedView, mView);
1047 assertReplyButtonShownWithEqualMeasures(
1048 expectedView.getChildAt(0), mView.getChildAt(0)); // a1
1049 assertReplyButtonShownWithEqualMeasures(
1050 expectedView.getChildAt(1), mView.getChildAt(1)); // a2
1051 assertReplyButtonHidden(mView.getChildAt(2)); // a3
1052 assertReplyButtonHidden(mView.getChildAt(3)); // a4
1053 }
1054
1055 /**
1056 * Test that setting maximum number of actions to -1 means there's no limit to number of actions
1057 * to show.
1058 */
1059 @Test
1060 public void testMeasure_maxNumActions_noLimit() {
1061 when(mConstants.getMaxNumActions()).thenReturn(-1);
1062
1063 String[] choices = new String[] {};
1064 String[] actions = new String[] {"a1", "a2", "a3", "a4"};
1065
1066 // All replies should be displayed as single-line smart reply buttons.
1067 ViewGroup expectedView = buildExpectedView(new String[] {},
1068 1 /* lineCount */, createActions(new String[] {"a1", "a2", "a3", "a4"}));
1069 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1070
1071 setSmartRepliesAndActions(choices, actions);
1072 mView.measure(
1073 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1074 MeasureSpec.UNSPECIFIED);
1075
1076 assertEqualMeasures(expectedView, mView);
1077 assertReplyButtonShownWithEqualMeasures(
1078 expectedView.getChildAt(0), mView.getChildAt(0)); // a1
1079 assertReplyButtonShownWithEqualMeasures(
1080 expectedView.getChildAt(1), mView.getChildAt(1)); // a2
1081 assertReplyButtonShownWithEqualMeasures(
1082 expectedView.getChildAt(2), mView.getChildAt(2)); // a3
1083 assertReplyButtonShownWithEqualMeasures(
1084 expectedView.getChildAt(3), mView.getChildAt(3)); // a4
1085 }
Petr Cermak9a3380c2018-01-19 15:00:24 +00001086}