blob: 0ce1df3d19f499d0b91e98b97efc9aa269744948 [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.testing.AndroidTestingRunner;
41import android.testing.TestableLooper;
Petr Cermak102431d2018-01-29 10:36:07 +000042import android.view.View;
43import android.view.ViewGroup;
44import android.widget.Button;
45import android.widget.LinearLayout;
Petr Cermak9a3380c2018-01-19 15:00:24 +000046
Brett Chabot84151d92019-02-27 15:37:59 -080047import androidx.test.filters.SmallTest;
48
Gustav Sennton13edb492019-01-28 21:40:04 +000049import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Petr Cermak102431d2018-01-29 10:36:07 +000050import com.android.systemui.R;
Petr Cermak9a3380c2018-01-19 15:00:24 +000051import com.android.systemui.SysuiTestCase;
Gustav Senntoneab53682018-11-01 16:30:23 +000052import com.android.systemui.plugins.ActivityStarter;
53import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
Kenny Guya0f6de82018-04-06 16:20:16 +010054import com.android.systemui.statusbar.SmartReplyController;
Ned Burnsf81c4c42019-01-07 14:10:43 -050055import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Milo Sredkovb0f55e92018-04-04 16:13:28 +010056import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
Gustav Senntoneab53682018-11-01 16:30:23 +000057import com.android.systemui.statusbar.phone.ShadeController;
Petr Cermak9a3380c2018-01-19 15:00:24 +000058
Petr Cermak7ef78122018-02-23 15:54:34 +000059import org.junit.After;
Petr Cermak9a3380c2018-01-19 15:00:24 +000060import org.junit.Before;
61import org.junit.Test;
62import org.junit.runner.RunWith;
Kenny Guy23991102018-04-05 21:18:38 +010063import org.mockito.Mock;
64import org.mockito.MockitoAnnotations;
Petr Cermak9a3380c2018-01-19 15:00:24 +000065
Gustav Senntoneab53682018-11-01 16:30:23 +000066import java.util.ArrayList;
Tony Mak7d4b3a52018-11-27 17:29:36 +000067import java.util.Collections;
Gustav Senntoneab53682018-11-01 16:30:23 +000068import java.util.List;
69import java.util.concurrent.atomic.AtomicReference;
70
Petr Cermak9a3380c2018-01-19 15:00:24 +000071@RunWith(AndroidTestingRunner.class)
72@TestableLooper.RunWithLooper
73@SmallTest
74public class SmartReplyViewTest extends SysuiTestCase {
Petr Cermak9a3380c2018-01-19 15:00:24 +000075 private static final String TEST_RESULT_KEY = "test_result_key";
Selim Cinek9a236f72018-02-22 18:35:53 -080076 private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
Petr Cermak102431d2018-01-29 10:36:07 +000077
Petr Cermak9a3380c2018-01-19 15:00:24 +000078 private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
Kenny Guya0f6de82018-04-06 16:20:16 +010079 private static final String TEST_NOTIFICATION_KEY = "akey";
Petr Cermak9a3380c2018-01-19 15:00:24 +000080
Gustav Senntoneab53682018-11-01 16:30:23 +000081 private static final String[] TEST_ACTION_TITLES = new String[]{
82 "First action", "Open something", "Action"
83 };
84
Petr Cermak102431d2018-01-29 10:36:07 +000085 private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
86 private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
87
Petr Cermak9a3380c2018-01-19 15:00:24 +000088 private BlockingQueueIntentReceiver mReceiver;
89 private SmartReplyView mView;
Kenny Guya0f6de82018-04-06 16:20:16 +010090 private View mContainer;
Petr Cermak9a3380c2018-01-19 15:00:24 +000091
Gustav Senntoneab53682018-11-01 16:30:23 +000092 private Icon mActionIcon;
93
Petr Cermak102431d2018-01-29 10:36:07 +000094 private int mSingleLinePaddingHorizontal;
95 private int mDoubleLinePaddingHorizontal;
96 private int mSpacing;
97
Kenny Guya0f6de82018-04-06 16:20:16 +010098 @Mock private SmartReplyController mLogger;
Ned Burnsf81c4c42019-01-07 14:10:43 -050099 private NotificationEntry mEntry;
Kenny Guya0f6de82018-04-06 16:20:16 +0100100 private Notification mNotification;
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000101 @Mock private SmartReplyConstants mConstants;
Kenny Guy23991102018-04-05 21:18:38 +0100102
Gustav Senntoneab53682018-11-01 16:30:23 +0000103 @Mock ActivityStarter mActivityStarter;
Gustav Senntond0e84532018-12-03 16:48:36 +0000104 @Mock HeadsUpManager mHeadsUpManager;
Gustav Senntoneab53682018-11-01 16:30:23 +0000105
Petr Cermak9a3380c2018-01-19 15:00:24 +0000106 @Before
107 public void setUp() {
Kenny Guy23991102018-04-05 21:18:38 +0100108 MockitoAnnotations.initMocks(this);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000109 mReceiver = new BlockingQueueIntentReceiver();
110 mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
Milo Sredkove433e9b2018-05-01 22:45:38 +0100111 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
Gustav Senntoneab53682018-11-01 16:30:23 +0000112 mDependency.injectMockDependency(ShadeController.class);
113 mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000114 mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000115
Kenny Guya0f6de82018-04-06 16:20:16 +0100116 mContainer = new View(mContext, null);
Gustav Sennton5759f872019-02-13 17:25:26 +0000117 mView = SmartReplyView.inflate(mContext);
Petr Cermak102431d2018-01-29 10:36:07 +0000118
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000119 // Any number of replies are fine.
120 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0);
121 when(mConstants.getMaxSqueezeRemeasureAttempts()).thenReturn(3);
Gustav Sennton4bf5ff52019-01-16 14:27:25 +0000122 when(mConstants.getMaxNumActions()).thenReturn(-1);
Petr Cermak102431d2018-01-29 10:36:07 +0000123
124 final Resources res = mContext.getResources();
125 mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
126 R.dimen.smart_reply_button_padding_horizontal_single_line);
127 mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
128 R.dimen.smart_reply_button_padding_horizontal_double_line);
129 mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
Kenny Guy23991102018-04-05 21:18:38 +0100130
Kenny Guya0f6de82018-04-06 16:20:16 +0100131 mNotification = new Notification.Builder(mContext, "")
132 .setSmallIcon(R.drawable.ic_person)
133 .setContentTitle("Title")
134 .setContentText("Text").build();
135 StatusBarNotification sbn = mock(StatusBarNotification.class);
136 when(sbn.getNotification()).thenReturn(mNotification);
137 when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500138 mEntry = new NotificationEntry(sbn);
Gustav Senntoneab53682018-11-01 16:30:23 +0000139
140 mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000141 }
142
Petr Cermak7ef78122018-02-23 15:54:34 +0000143 @After
144 public void tearDown() {
145 mContext.unregisterReceiver(mReceiver);
146 }
147
Petr Cermak9a3380c2018-01-19 15:00:24 +0000148 @Test
149 public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
Gustav Senntoneab53682018-11-01 16:30:23 +0000150 setSmartReplies(TEST_CHOICES);
Petr Cermak9a3380c2018-01-19 15:00:24 +0000151
152 mView.getChildAt(2).performClick();
153
154 Intent resultIntent = mReceiver.waitForIntent();
155 assertEquals(TEST_CHOICES[2],
156 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
157 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
158 }
Petr Cermak102431d2018-01-29 10:36:07 +0000159
160 @Test
Milo Sredkovb0f55e92018-04-04 16:13:28 +0100161 public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
Milo Sredkove433e9b2018-05-01 22:45:38 +0100162 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
Gustav Senntoneab53682018-11-01 16:30:23 +0000163 setSmartReplies(TEST_CHOICES);
Milo Sredkovb0f55e92018-04-04 16:13:28 +0100164
165 mView.getChildAt(2).performClick();
166
167 assertNull(mReceiver.waitForIntent());
168 }
169
170 @Test
171 public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
172 AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
Milo Sredkove433e9b2018-05-01 22:45:38 +0100173 mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
Gustav Senntoneab53682018-11-01 16:30:23 +0000174 setSmartReplies(TEST_CHOICES);
Milo Sredkovb0f55e92018-04-04 16:13:28 +0100175
176 mView.getChildAt(2).performClick();
177
178 // No intent until the screen is unlocked.
179 assertNull(mReceiver.waitForIntent());
180
181 actionRef.get().onDismiss();
182
183 // Now the intent should arrive.
184 Intent resultIntent = mReceiver.waitForIntent();
185 assertEquals(TEST_CHOICES[2],
186 RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
187 assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
188 }
189
190 @Test
Kenny Guya0f6de82018-04-06 16:20:16 +0100191 public void testSendSmartReply_controllerCalled() {
Gustav Senntoneab53682018-11-01 16:30:23 +0000192 setSmartReplies(TEST_CHOICES);
Kenny Guy23991102018-04-05 21:18:38 +0100193 mView.getChildAt(2).performClick();
Tony Mak29996702018-11-26 16:23:34 +0000194 verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
Milo Sredkov13d88112019-02-01 12:23:24 +0000195 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
Kenny Guya0f6de82018-04-06 16:20:16 +0100196 }
197
198 @Test
199 public void testSendSmartReply_hidesContainer() {
200 mContainer.setVisibility(View.VISIBLE);
Gustav Senntoneab53682018-11-01 16:30:23 +0000201 setSmartReplies(TEST_CHOICES);
Kenny Guya0f6de82018-04-06 16:20:16 +0100202 mView.getChildAt(0).performClick();
203 assertEquals(View.GONE, mContainer.getVisibility());
Kenny Guy23991102018-04-05 21:18:38 +0100204 }
205
206 @Test
Petr Cermak102431d2018-01-29 10:36:07 +0000207 public void testMeasure_empty() {
208 mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
209 assertEquals(500, mView.getMeasuredWidthAndState());
210 assertEquals(0, mView.getMeasuredHeightAndState());
211 }
212
213 @Test
214 public void testLayout_empty() {
215 mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
216 mView.layout(0, 0, 500, 0);
217 }
218
219
220 // Instead of manually calculating the expected measurement/layout results, we build the
221 // expectations as ordinary linear layouts and then check that the relevant parameters in the
222 // corresponding SmartReplyView and LinearView are equal.
223
224 @Test
225 public void testMeasure_shortChoices() {
226 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello", "Bye"};
227
228 // All choices should be displayed as SINGLE-line smart reply buttons.
229 ViewGroup expectedView = buildExpectedView(choices, 1);
230 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
231
Gustav Senntoneab53682018-11-01 16:30:23 +0000232 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000233 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
234
235 assertEqualMeasures(expectedView, mView);
236 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
237 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
238 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
239 }
240
241 @Test
242 public void testLayout_shortChoices() {
243 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello", "Bye"};
244
245 // All choices should be displayed as SINGLE-line smart reply buttons.
246 ViewGroup expectedView = buildExpectedView(choices, 1);
247 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
248 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
249 10 + expectedView.getMeasuredHeight());
250
Gustav Senntoneab53682018-11-01 16:30:23 +0000251 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000252 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
253 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
254
255 assertEqualLayouts(expectedView, mView);
256 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
257 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
258 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
259 }
260
261 @Test
262 public void testMeasure_choiceWithTwoLines() {
263 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\neveryone", "Bye"};
264
265 // All choices should be displayed as DOUBLE-line smart reply buttons.
266 ViewGroup expectedView = buildExpectedView(choices, 2);
267 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
268
Gustav Senntoneab53682018-11-01 16:30:23 +0000269 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000270 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
271
272 assertEqualMeasures(expectedView, mView);
273 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
274 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
275 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
276 }
277
278 @Test
279 public void testLayout_choiceWithTwoLines() {
280 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\neveryone", "Bye"};
281
282 // All choices should be displayed as DOUBLE-line smart reply buttons.
283 ViewGroup expectedView = buildExpectedView(choices, 2);
284 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
285 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
286 10 + expectedView.getMeasuredHeight());
287
Gustav Senntoneab53682018-11-01 16:30:23 +0000288 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000289 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
290 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
291
292 assertEqualLayouts(expectedView, mView);
293 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
294 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
295 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
296 }
297
298 @Test
299 public void testMeasure_choiceWithThreeLines() {
300 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
301
302 // The choice with three lines should NOT be displayed. All other choices should be
303 // displayed as SINGLE-line smart reply buttons.
304 ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
305 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
306
Gustav Senntoneab53682018-11-01 16:30:23 +0000307 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000308 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
309
310 assertEqualMeasures(expectedView, mView);
311 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
312 assertReplyButtonHidden(mView.getChildAt(1));
313 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
314 }
315
316 @Test
317 public void testLayout_choiceWithThreeLines() {
318 final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
319
320 // The choice with three lines should NOT be displayed. All other choices should be
321 // displayed as SINGLE-line smart reply buttons.
322 ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
323 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
324 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
325 10 + expectedView.getMeasuredHeight());
326
Gustav Senntoneab53682018-11-01 16:30:23 +0000327 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000328 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
329 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
330
331 assertEqualLayouts(expectedView, mView);
332 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
333 // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
334 // testMeasure_choiceWithThreeLines).
335 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
336 }
337
338 @Test
339 public void testMeasure_squeezeLongest() {
340 final CharSequence[] choices = new CharSequence[]{"Short", "Short", "Looooooong replyyyyy"};
341
342 // All choices should be displayed as DOUBLE-line smart reply buttons.
343 ViewGroup expectedView = buildExpectedView(
344 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
345 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
346
Gustav Senntoneab53682018-11-01 16:30:23 +0000347 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000348 mView.measure(
349 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
350 MeasureSpec.UNSPECIFIED);
351
352 assertEqualMeasures(expectedView, mView);
353 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
354 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
355 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
356 }
357
358 @Test
359 public void testLayout_squeezeLongest() {
360 final CharSequence[] choices = new CharSequence[]{"Short", "Short", "Looooooong replyyyyy"};
361
362 // All choices should be displayed as DOUBLE-line smart reply buttons.
363 ViewGroup expectedView = buildExpectedView(
364 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
365 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
366 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
367 10 + expectedView.getMeasuredHeight());
368
Gustav Senntoneab53682018-11-01 16:30:23 +0000369 setSmartReplies(choices);
Petr Cermak102431d2018-01-29 10:36:07 +0000370 mView.measure(
371 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
372 MeasureSpec.UNSPECIFIED);
373 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
374
375 assertEqualLayouts(expectedView, mView);
376 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
377 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
378 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
379 }
380
Kenny Guy48ee6d62018-05-09 16:51:26 +0100381 @Test
382 public void testMeasure_dropLongest() {
383 final CharSequence[] choices = new CharSequence[]{"Short", "Short",
384 "LooooooongUnbreakableReplyyyyy"};
385
386 // Short choices should be shown as single line views
387 ViewGroup expectedView = buildExpectedView(
388 new CharSequence[]{"Short", "Short"}, 1);
389 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
390 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
391 10 + expectedView.getMeasuredHeight());
392
Gustav Senntoneab53682018-11-01 16:30:23 +0000393 setSmartReplies(choices);
Kenny Guy48ee6d62018-05-09 16:51:26 +0100394 mView.measure(
395 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
396 MeasureSpec.UNSPECIFIED);
397 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
398
399 assertEqualLayouts(expectedView, mView);
400 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
401 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
402 assertReplyButtonHidden(mView.getChildAt(2));
403 }
404
Gustav Senntoneab53682018-11-01 16:30:23 +0000405 private void setSmartReplies(CharSequence[] choices) {
Gustav Sennton5759f872019-02-13 17:25:26 +0000406 mView.resetSmartSuggestions(mContainer);
407 List<Button> replyButtons = inflateSmartReplies(choices, false /* fromAssistant */);
408 mView.addPreInflatedButtons(replyButtons);
Tony Mak29996702018-11-26 16:23:34 +0000409 }
410
Gustav Sennton5759f872019-02-13 17:25:26 +0000411 private List<Button> inflateSmartReplies(CharSequence[] choices, boolean fromAssistant) {
Petr Cermak102431d2018-01-29 10:36:07 +0000412 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
413 new Intent(TEST_ACTION), 0);
414 RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
Tony Mak29996702018-11-26 16:23:34 +0000415 SmartReplyView.SmartReplies smartReplies =
416 new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
Gustav Sennton5759f872019-02-13 17:25:26 +0000417 return mView.inflateRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
Gustav Senntoneab53682018-11-01 16:30:23 +0000418 }
419
420 private Notification.Action createAction(String actionTitle) {
421 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
422 new Intent(TEST_ACTION), 0);
423 return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
424 }
425
426 private List<Notification.Action> createActions(String[] actionTitles) {
427 List<Notification.Action> actions = new ArrayList<>();
428 for (String title : actionTitles) {
429 actions.add(createAction(title));
430 }
431 return actions;
432 }
433
434 private void setSmartActions(String[] actionTitles) {
435 mView.resetSmartSuggestions(mContainer);
Gustav Sennton5759f872019-02-13 17:25:26 +0000436 List<Button> actions = mView.inflateSmartActions(
Tony Mak7d4b3a52018-11-27 17:29:36 +0000437 new SmartReplyView.SmartActions(createActions(actionTitles), false),
438 mLogger,
Gustav Senntond0e84532018-12-03 16:48:36 +0000439 mEntry,
440 mHeadsUpManager);
Gustav Sennton5759f872019-02-13 17:25:26 +0000441 mView.addPreInflatedButtons(actions);
Gustav Senntoneab53682018-11-01 16:30:23 +0000442 }
443
444 private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000445 setSmartRepliesAndActions(choices, actionTitles, false /* fromAssistant */);
446 }
447
448 private void setSmartRepliesAndActions(
449 CharSequence[] choices, String[] actionTitles, boolean fromAssistant) {
Gustav Sennton5759f872019-02-13 17:25:26 +0000450 mView.resetSmartSuggestions(mContainer);
451 List<Button> smartSuggestions = inflateSmartReplies(choices, fromAssistant);
452 smartSuggestions.addAll(mView.inflateSmartActions(
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000453 new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
Tony Mak7d4b3a52018-11-27 17:29:36 +0000454 mLogger,
Gustav Senntond0e84532018-12-03 16:48:36 +0000455 mEntry,
Gustav Sennton5759f872019-02-13 17:25:26 +0000456 mHeadsUpManager));
457 mView.addPreInflatedButtons(smartSuggestions);
Gustav Senntoneab53682018-11-01 16:30:23 +0000458 }
459
460 private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
461 return buildExpectedView(choices, lineCount, new ArrayList<>());
Petr Cermak102431d2018-01-29 10:36:07 +0000462 }
463
464 /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
Gustav Senntoneab53682018-11-01 16:30:23 +0000465 private ViewGroup buildExpectedView(
466 CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
Petr Cermak102431d2018-01-29 10:36:07 +0000467 LinearLayout layout = new LinearLayout(mContext);
468 layout.setOrientation(LinearLayout.HORIZONTAL);
469
470 // Baseline alignment causes expected heights to be off by one or two pixels on some
471 // devices.
472 layout.setBaselineAligned(false);
473
474 final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
475 final int paddingHorizontal;
476 switch (lineCount) {
477 case 1:
478 paddingHorizontal = mSingleLinePaddingHorizontal;
479 break;
480 case 2:
481 paddingHorizontal = mDoubleLinePaddingHorizontal;
482 break;
483 default:
484 fail("Invalid line count " + lineCount);
485 return null;
486 }
487
Gustav Senntoneab53682018-11-01 16:30:23 +0000488 // Add smart replies
Petr Cermak102431d2018-01-29 10:36:07 +0000489 Button previous = null;
Tony Mak29996702018-11-26 16:23:34 +0000490 SmartReplyView.SmartReplies smartReplies =
491 new SmartReplyView.SmartReplies(choices, null, null, false);
Kenny Guy23991102018-04-05 21:18:38 +0100492 for (int i = 0; i < choices.length; ++i) {
Gustav Sennton5759f872019-02-13 17:25:26 +0000493 Button current = SmartReplyView.inflateReplyButton(mView, mContext, i, smartReplies,
494 null /* SmartReplyController */, null /* NotificationEntry */);
Petr Cermak102431d2018-01-29 10:36:07 +0000495 current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
496 current.getPaddingBottom());
497 if (previous != null) {
498 ViewGroup.MarginLayoutParams lp =
499 (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
500 if (isRtl) {
501 lp.leftMargin = mSpacing;
502 } else {
503 lp.rightMargin = mSpacing;
504 }
505 }
506 layout.addView(current);
507 previous = current;
508 }
509
Gustav Senntoneab53682018-11-01 16:30:23 +0000510 // Add smart actions
511 for (int i = 0; i < actions.size(); ++i) {
512 Button current = inflateActionButton(actions.get(i));
513 current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
514 current.getPaddingBottom());
515 if (previous != null) {
516 ViewGroup.MarginLayoutParams lp =
517 (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
518 if (isRtl) {
519 lp.leftMargin = mSpacing;
520 } else {
521 lp.rightMargin = mSpacing;
522 }
523 }
524 layout.addView(current);
525 previous = current;
526 }
527
Petr Cermak102431d2018-01-29 10:36:07 +0000528 return layout;
529 }
530
531 private static void assertEqualMeasures(View expected, View actual) {
532 assertEquals(expected.getMeasuredWidth(), actual.getMeasuredWidth());
533 assertEquals(expected.getMeasuredHeight(), actual.getMeasuredHeight());
534 }
535
536 private static void assertReplyButtonShownWithEqualMeasures(View expected, View actual) {
537 assertReplyButtonShown(actual);
538 assertEqualMeasures(expected, actual);
Kenny Guy48ee6d62018-05-09 16:51:26 +0100539 assertEqualPadding(expected, actual);
Petr Cermak102431d2018-01-29 10:36:07 +0000540 }
541
542 private static void assertReplyButtonShown(View view) {
543 assertTrue(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
544 }
545
546 private static void assertReplyButtonHidden(View view) {
547 assertFalse(((SmartReplyView.LayoutParams) view.getLayoutParams()).isShown());
548 }
549
550 private static void assertEqualLayouts(View expected, View actual) {
551 assertEquals(expected.getLeft(), actual.getLeft());
552 assertEquals(expected.getTop(), actual.getTop());
553 assertEquals(expected.getRight(), actual.getRight());
554 assertEquals(expected.getBottom(), actual.getBottom());
555 }
Kenny Guy48ee6d62018-05-09 16:51:26 +0100556
557 private static void assertEqualPadding(View expected, View actual) {
558 assertEquals(expected.getPaddingLeft(), actual.getPaddingLeft());
559 assertEquals(expected.getPaddingTop(), actual.getPaddingTop());
560 assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
561 assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
562 }
Gustav Senntoneab53682018-11-01 16:30:23 +0000563
564
565 // =============================================================================================
566 // ============================= Smart Action tests ============================================
567 // =============================================================================================
568
569 @Test
570 public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
571 setSmartActions(TEST_ACTION_TITLES);
572
573 mView.getChildAt(2).performClick();
574
Tony Mak7d4b3a52018-11-27 17:29:36 +0000575 verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
Gustav Senntoneab53682018-11-01 16:30:23 +0000576 }
577
578 @Test
579 public void testMeasure_shortSmartActions() {
580 String[] actions = new String[] {"Hi", "Hello", "Bye"};
581 // All choices should be displayed as SINGLE-line smart action buttons.
582 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
583 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
584
585 setSmartActions(actions);
586 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
587
588 assertEqualMeasures(expectedView, mView);
589 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
590 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
591 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
592 }
593
594 @Test
595 public void testLayout_shortSmartActions() {
596 String[] actions = new String[] {"Hi", "Hello", "Bye"};
597 // All choices should be displayed as SINGLE-line smart action buttons.
598 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
599 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
600 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
601 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
602 10 + expectedView.getMeasuredHeight());
603
604 setSmartActions(actions);
605 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
606 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
607
608 assertEqualLayouts(expectedView, mView);
609 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
610 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
611 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
612 }
613
614 @Test
615 public void testMeasure_smartActionWithTwoLines() {
616 String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
617
618 // All actions should be displayed as DOUBLE-line smart action buttons.
619 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
620 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
621
622 setSmartActions(actions);
623 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
624
625 assertEqualMeasures(expectedView, mView);
626 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
627 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
628 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
629 }
630
631 @Test
632 public void testLayout_smartActionWithTwoLines() {
633 String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
634
635 // All actions should be displayed as DOUBLE-line smart action buttons.
636 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
637 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
638 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
639 10 + expectedView.getMeasuredHeight());
640
641 setSmartActions(actions);
642 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
643 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
644
645 assertEqualLayouts(expectedView, mView);
646 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
647 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
648 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
649 }
650
651 @Test
652 public void testMeasure_smartActionWithThreeLines() {
653 String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
654
655 // The action with three lines should NOT be displayed. All other actions should be
656 // displayed as SINGLE-line smart action buttons.
657 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
658 createActions(new String[]{"Hi", "Bye"}));
659 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
660
661 setSmartActions(actions);
662 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
663
664 assertEqualMeasures(expectedView, mView);
665 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
666 assertReplyButtonHidden(mView.getChildAt(1));
667 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
668 }
669
670 @Test
671 public void testLayout_smartActionWithThreeLines() {
672 String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
673
674 // The action with three lines should NOT be displayed. All other actions should be
675 // displayed as SINGLE-line smart action buttons.
676 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
677 createActions(new String[]{"Hi", "Bye"}));
678 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
679 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
680 10 + expectedView.getMeasuredHeight());
681
682 setSmartActions(actions);
683 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
684 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
685
686 assertEqualLayouts(expectedView, mView);
687 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
688 // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
689 // testMeasure_smartActionWithThreeLines).
690 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
691 }
692
693 @Test
694 public void testMeasure_squeezeLongestSmartAction() {
695 String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
696
697 // All actions should be displayed as DOUBLE-line smart action buttons.
698 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
699 createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
700 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
701
702 setSmartActions(actions);
703 mView.measure(
704 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
705 MeasureSpec.UNSPECIFIED);
706
707 assertEqualMeasures(expectedView, mView);
708 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
709 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
710 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
711 }
712
713 @Test
714 public void testLayout_squeezeLongestSmartAction() {
715 String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
716
717 // All actions should be displayed as DOUBLE-line smart action buttons.
718 ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
719 createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
720 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
721 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
722 10 + expectedView.getMeasuredHeight());
723
724 setSmartActions(actions);
725 mView.measure(
726 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
727 MeasureSpec.UNSPECIFIED);
728 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
729
730 assertEqualLayouts(expectedView, mView);
731 assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
732 assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
733 assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
734 }
735
736 @Test
737 public void testMeasure_dropLongestSmartAction() {
738 String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
739
740 // Short actions should be shown as single line views
741 ViewGroup expectedView = buildExpectedView(
742 new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
743 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
744 expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
745 10 + expectedView.getMeasuredHeight());
746
747 setSmartActions(actions);
748 mView.measure(
749 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
750 MeasureSpec.UNSPECIFIED);
751 mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
752
753 assertEqualLayouts(expectedView, mView);
754 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
755 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
756 assertReplyButtonHidden(mView.getChildAt(2));
757 }
758
759 private Button inflateActionButton(Notification.Action action) {
Gustav Sennton5759f872019-02-13 17:25:26 +0000760 return SmartReplyView.inflateActionButton(mView, getContext(), 0,
Tony Mak7d4b3a52018-11-27 17:29:36 +0000761 new SmartReplyView.SmartActions(Collections.singletonList(action), false),
Gustav Senntond0e84532018-12-03 16:48:36 +0000762 mLogger, mEntry, mHeadsUpManager);
Gustav Senntoneab53682018-11-01 16:30:23 +0000763 }
764
765 @Test
766 public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
767 // Ensure smart action icons are the same size regardless of the number of text rows in the
768 // button.
769 Button singleLineButton = inflateActionButton(createAction("One line"));
770 Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
771 Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
772 Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
773 assertEquals(singleLineDrawable.getBounds().width(),
774 doubleLineDrawable.getBounds().width());
775 assertEquals(singleLineDrawable.getBounds().height(),
776 doubleLineDrawable.getBounds().height());
777 }
778
779 @Test
780 public void testMeasure_shortChoicesAndActions() {
781 CharSequence[] choices = new String[] {"Hi", "Hello"};
782 String[] actions = new String[] {"Bye"};
783 // All choices should be displayed as SINGLE-line smart action buttons.
784 ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
785 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
786
787 setSmartRepliesAndActions(choices, actions);
788 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
789
790 assertEqualMeasures(expectedView, mView);
791 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
792 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
793 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
794 }
795
796 @Test
797 public void testMeasure_choicesAndActionsSqueezeLongestAction() {
798 CharSequence[] choices = new String[] {"Short", "Short"};
799 String[] actions = new String[] {"Looooooong replyyyyy"};
800
801 // All actions should be displayed as DOUBLE-line smart action buttons.
802 ViewGroup expectedView = buildExpectedView(choices, 2,
803 createActions(new String[] {"Looooooong \nreplyyyyy"}));
804 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
805
806 setSmartRepliesAndActions(choices, actions);
807 mView.measure(
808 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
809 MeasureSpec.UNSPECIFIED);
810
811 assertEqualMeasures(expectedView, mView);
812 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
813 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
814 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
815 }
Gustav Senntonb149a1a2018-11-20 17:25:50 +0000816
817 @Test
818 public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
819 String[] choices = new String[] {"Reply"};
820 String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
821
822 // All actions should be displayed as DOUBLE-line smart action buttons.
823 ViewGroup expectedView = buildExpectedView(new String[0], 2,
824 createActions(new String[] {
825 "Looooooong \nactioooon", "second \naction", "third \naction"}));
826 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
827
828 setSmartRepliesAndActions(choices, actions);
829 mView.measure(
830 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
831 MeasureSpec.UNSPECIFIED);
832
833 assertEqualMeasures(expectedView, mView);
834 // smart replies
835 assertReplyButtonHidden(mView.getChildAt(0));
836 // smart actions
837 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
838 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
839 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
840 }
841
842 @Test
843 public void testMeasure_choicesAndActionsPrioritizeActions() {
844 String[] choices = new String[] {"Short", "longer reply"};
845 String[] actions = new String[] {"Looooooong actioooon", "second action"};
846
847 // All actions should be displayed as DOUBLE-line smart action buttons.
848 ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
849 createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
850 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
851
852 setSmartRepliesAndActions(choices, actions);
853 mView.measure(
854 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
855 MeasureSpec.UNSPECIFIED);
856
857 Button firstAction = ((Button) mView.getChildAt(1));
858
859 assertEqualMeasures(expectedView, mView);
860 // smart replies
861 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
862 assertReplyButtonHidden(mView.getChildAt(1));
863 // smart actions
864 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
865 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
866 }
Gustav Sennton35156752018-12-20 10:35:03 +0000867
868 /**
869 * Test to ensure that we try to add all possible actions - if we find one action that's too
870 * long we just skip that one rather than quitting altogether.
871 */
872 @Test
873 public void testMeasure_skipTooLongActions() {
874 String[] choices = new String[] {};
875 String[] actions = new String[] {
876 "a1", "a2", "this action is soooooooo long it's ridiculous", "a4"};
877
878 // All actions should be displayed as DOUBLE-line smart action buttons.
879 ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
880 createActions(new String[] {"a1", "a2", "a4"}));
881 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
882
883 setSmartRepliesAndActions(choices, actions);
884 mView.measure(
885 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
886 MeasureSpec.UNSPECIFIED);
887
888 assertEqualMeasures(expectedView, mView);
889 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
890 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
891 assertReplyButtonHidden(mView.getChildAt(2));
892 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
893 }
894
895 /**
896 * Test to ensure that we try to add all possible replies - if we find one reply that's too
897 * long we just skip that one rather than quitting altogether.
898 */
899 @Test
900 public void testMeasure_skipTooLongReplies() {
901 String[] choices = new String[] {
902 "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
903 String[] actions = new String[] {};
904
905 // All replies should be displayed as single-line smart reply buttons.
906 ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
907 1 /* lineCount */, Collections.emptyList());
908 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
909
910 setSmartRepliesAndActions(choices, actions);
911 mView.measure(
912 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
913 MeasureSpec.UNSPECIFIED);
914
915 assertEqualMeasures(expectedView, mView);
916 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
917 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
918 assertReplyButtonHidden(mView.getChildAt(2));
919 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
920 }
921
922 /**
923 * Test to ensure that we try to add all possible replies and actions - if we find a reply or
924 * action that's too long we just skip that one rather than quitting altogether.
925 */
926 @Test
927 public void testMeasure_skipTooLongRepliesAndActions() {
928 String[] choices = new String[] {
929 "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
930 String[] actions = new String[] {
931 "a1", "ThisActionIsSooooooooLongItsRidiculousIPromise"};
932
933 // All replies should be displayed as single-line smart reply buttons.
934 ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
935 1 /* lineCount */, createActions(new String[] {"a1"}));
936 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
937
938 setSmartRepliesAndActions(choices, actions);
939 mView.measure(
940 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
941 MeasureSpec.UNSPECIFIED);
942
943 assertEqualMeasures(expectedView, mView);
944 assertReplyButtonShownWithEqualMeasures(
945 expectedView.getChildAt(0), mView.getChildAt(0)); // r1
946 assertReplyButtonShownWithEqualMeasures(
947 expectedView.getChildAt(1), mView.getChildAt(1)); // r2
948 assertReplyButtonHidden(mView.getChildAt(2)); // long reply
949 assertReplyButtonShownWithEqualMeasures(
950 expectedView.getChildAt(2), mView.getChildAt(3)); // r4
951 assertReplyButtonShownWithEqualMeasures(
952 expectedView.getChildAt(3), mView.getChildAt(4)); // a1
953 assertReplyButtonHidden(mView.getChildAt(5)); // long action
954 }
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000955
956 @Test
957 public void testMeasure_minNumSystemGeneratedSmartReplies_notEnoughReplies() {
958 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(3);
959
960 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
961 String[] choices = new String[] {"reply1", "reply2"};
962 String[] actions = new String[] {"action1"};
963
964 ViewGroup expectedView = buildExpectedView(new String[] {}, 1,
965 createActions(new String[] {"action1"}));
966 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
967
968 setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
969 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
970
971 assertEqualMeasures(expectedView, mView);
972 // smart replies
973 assertReplyButtonHidden(mView.getChildAt(0));
974 assertReplyButtonHidden(mView.getChildAt(1));
975 // smart actions
976 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(2));
977 }
978
979 @Test
980 public void testMeasure_minNumSystemGeneratedSmartReplies_enoughReplies() {
981 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
982
983 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
984 String[] choices = new String[] {"reply1", "reply2"};
985 String[] actions = new String[] {"action1"};
986
987 ViewGroup expectedView = buildExpectedView(new String[] {"reply1", "reply2"}, 1,
988 createActions(new String[] {"action1"}));
989 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
990
991 setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
992 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
993
994 assertEqualMeasures(expectedView, mView);
995 // smart replies
996 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
997 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
998 // smart actions
999 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
1000 }
1001
1002 /**
1003 * Ensure actions that are squeezed when shown together with smart replies are unsqueezed if the
1004 * replies are never added (because of the SmartReplyConstants.getMinNumSystemGeneratedReplies()
1005 * flag).
1006 */
1007 @Test
1008 public void testMeasure_minNumSystemGeneratedSmartReplies_unSqueezeActions() {
1009 when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
1010
1011 // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
1012 String[] choices = new String[] {"This is a very long two-line reply."};
1013 String[] actions = new String[] {"Short action"};
1014
1015 // The action should be displayed on one line only - since it fits!
1016 ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
1017 createActions(new String[] {"Short action"}));
1018 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1019
1020 setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
1021 mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1022
1023 assertEqualMeasures(expectedView, mView);
1024 // smart replies
1025 assertReplyButtonHidden(mView.getChildAt(0));
1026 // smart actions
1027 assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
1028 }
Gustav Sennton4bf5ff52019-01-16 14:27:25 +00001029
1030 /**
1031 * Test that we don't show more than the maximum number of actions declared in {@link
1032 * SmartReplyConstants}.
1033 */
1034 @Test
1035 public void testMeasure_maxNumActions() {
1036 when(mConstants.getMaxNumActions()).thenReturn(2);
1037
1038 String[] choices = new String[] {};
1039 String[] actions = new String[] {"a1", "a2", "a3", "a4"};
1040
1041 // All replies should be displayed as single-line smart reply buttons.
1042 ViewGroup expectedView = buildExpectedView(new String[] {},
1043 1 /* lineCount */, createActions(new String[] {"a1", "a2"}));
1044 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1045
1046 setSmartRepliesAndActions(choices, actions);
1047 mView.measure(
1048 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1049 MeasureSpec.UNSPECIFIED);
1050
1051 assertEqualMeasures(expectedView, mView);
1052 assertReplyButtonShownWithEqualMeasures(
1053 expectedView.getChildAt(0), mView.getChildAt(0)); // a1
1054 assertReplyButtonShownWithEqualMeasures(
1055 expectedView.getChildAt(1), mView.getChildAt(1)); // a2
1056 assertReplyButtonHidden(mView.getChildAt(2)); // a3
1057 assertReplyButtonHidden(mView.getChildAt(3)); // a4
1058 }
1059
1060 /**
1061 * Test that setting maximum number of actions to -1 means there's no limit to number of actions
1062 * to show.
1063 */
1064 @Test
1065 public void testMeasure_maxNumActions_noLimit() {
1066 when(mConstants.getMaxNumActions()).thenReturn(-1);
1067
1068 String[] choices = new String[] {};
1069 String[] actions = new String[] {"a1", "a2", "a3", "a4"};
1070
1071 // All replies should be displayed as single-line smart reply buttons.
1072 ViewGroup expectedView = buildExpectedView(new String[] {},
1073 1 /* lineCount */, createActions(new String[] {"a1", "a2", "a3", "a4"}));
1074 expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
1075
1076 setSmartRepliesAndActions(choices, actions);
1077 mView.measure(
1078 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
1079 MeasureSpec.UNSPECIFIED);
1080
1081 assertEqualMeasures(expectedView, mView);
1082 assertReplyButtonShownWithEqualMeasures(
1083 expectedView.getChildAt(0), mView.getChildAt(0)); // a1
1084 assertReplyButtonShownWithEqualMeasures(
1085 expectedView.getChildAt(1), mView.getChildAt(1)); // a2
1086 assertReplyButtonShownWithEqualMeasures(
1087 expectedView.getChildAt(2), mView.getChildAt(2)); // a3
1088 assertReplyButtonShownWithEqualMeasures(
1089 expectedView.getChildAt(3), mView.getChildAt(3)); // a4
1090 }
Petr Cermak9a3380c2018-01-19 15:00:24 +00001091}