1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. */ 15 16 package com.android.systemui.statusbar.policy; 17 18 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; 19 20 import static com.google.common.truth.Truth.assertThat; 21 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.ArgumentMatchers.argThat; 25 import static org.mockito.Mockito.when; 26 27 import android.app.Notification; 28 import android.app.PendingIntent; 29 import android.app.RemoteInput; 30 import android.content.Intent; 31 import android.content.pm.ActivityInfo; 32 import android.content.pm.ResolveInfo; 33 import android.graphics.drawable.Icon; 34 import android.util.Pair; 35 36 import androidx.test.annotation.UiThreadTest; 37 import androidx.test.filters.SmallTest; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.systemui.SysuiTestCase; 41 import com.android.systemui.res.R; 42 import com.android.systemui.shared.system.ActivityManagerWrapper; 43 import com.android.systemui.shared.system.DevicePolicyManagerWrapper; 44 import com.android.systemui.shared.system.PackageManagerWrapper; 45 import com.android.systemui.statusbar.NotificationEntryHelper; 46 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 47 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; 48 import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions; 49 import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions; 50 import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies; 51 52 import org.junit.Before; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.mockito.Mock; 56 import org.mockito.MockitoAnnotations; 57 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.List; 61 62 @SmallTest 63 @RunWith(AndroidJUnit4.class) 64 public class InflatedSmartRepliesTest extends SysuiTestCase { 65 66 private static final Intent TEST_INTENT = new Intent("com.android.SMART_REPLY_VIEW_ACTION"); 67 private static final Intent ALLOWLISTED_TEST_INTENT = 68 new Intent("com.android.WHITELISTED_TEST_ACTION"); 69 70 @Mock private SmartReplyConstants mSmartReplyConstants; 71 @Mock private Notification mNotification; 72 @Mock private RemoteInput mRemoteInput; 73 @Mock private RemoteInput mFreeFormRemoteInput; 74 @Mock private ActivityManagerWrapper mActivityManagerWrapper; 75 @Mock private PackageManagerWrapper mPackageManagerWrapper; 76 @Mock private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper; 77 @Mock private SmartReplyInflater mSmartReplyInflater; 78 @Mock private SmartActionInflater mSmartActionInflater; 79 80 private Icon mActionIcon; 81 private NotificationEntry mEntry; 82 private SmartReplyStateInflaterImpl mSmartReplyStateInflater; 83 84 @Before 85 @UiThreadTest setUp()86 public void setUp() { 87 MockitoAnnotations.initMocks(this); 88 89 mDependency.injectTestDependency(ActivityManagerWrapper.class, mActivityManagerWrapper); 90 mDependency.injectTestDependency( 91 DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper); 92 mDependency.injectTestDependency(PackageManagerWrapper.class, mPackageManagerWrapper); 93 94 when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true); 95 mEntry = new NotificationEntryBuilder() 96 .setNotification(mNotification) 97 .build(); 98 when(mSmartReplyConstants.isEnabled()).thenReturn(true); 99 mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); 100 101 when(mActivityManagerWrapper.isLockTaskKioskModeActive()).thenReturn(false); 102 103 mSmartReplyStateInflater = new SmartReplyStateInflaterImpl( 104 mSmartReplyConstants, 105 mActivityManagerWrapper, 106 mPackageManagerWrapper, 107 mDevicePolicyManagerWrapper, 108 mSmartReplyInflater, 109 mSmartActionInflater); 110 } 111 112 @Test chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions()113 public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions() { 114 CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; 115 List<Notification.Action> smartActions = 116 createActions("Test Action 1", "Test Action 2"); 117 setupAppGeneratedSuggestions(smartReplies, smartActions); 118 when(mSmartReplyConstants.isEnabled()).thenReturn(false); 119 120 InflatedSmartReplyState smartReplyState = 121 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 122 123 assertThat(smartReplyState.getSmartReplies()).isNull(); 124 assertThat(smartReplyState.getSmartActions()).isNull(); 125 assertThat(smartReplyState.getSuppressedActions()).isNull(); 126 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 127 } 128 129 @Test chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions()130 public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() { 131 modifyRanking(mEntry) 132 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 133 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) 134 .build(); 135 136 when(mSmartReplyConstants.isEnabled()).thenReturn(false); 137 138 InflatedSmartReplyState smartReplyState = 139 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 140 141 assertThat(smartReplyState.getSmartReplies()).isNull(); 142 assertThat(smartReplyState.getSmartActions()).isNull(); 143 assertThat(smartReplyState.getSuppressedActions()).isNull(); 144 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 145 } 146 147 @Test chooseSmartRepliesAndActions_appGeneratedSmartReplies()148 public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() { 149 CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; 150 setupAppGeneratedReplies(smartReplies); 151 152 InflatedSmartReplyState smartReplyState = 153 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 154 155 assertThat(smartReplyState.getSmartReplies().choices) 156 .containsExactlyElementsIn(smartReplies).inOrder(); 157 assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse(); 158 assertThat(smartReplyState.getSmartActions()).isNull(); 159 assertThat(smartReplyState.getSuppressedActions()).isNull(); 160 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 161 } 162 163 @Test chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions()164 public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() { 165 CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; 166 List<Notification.Action> smartActions = 167 createActions("Test Action 1", "Test Action 2"); 168 setupAppGeneratedSuggestions(smartReplies, smartActions); 169 170 InflatedSmartReplyState smartReplyState = 171 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 172 173 assertThat(smartReplyState.getSmartReplies().choices) 174 .containsExactlyElementsIn(smartReplies).inOrder(); 175 assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse(); 176 assertThat(smartReplyState.getSmartActions().actions) 177 .containsExactlyElementsIn(smartActions).inOrder(); 178 assertThat(smartReplyState.getSmartActions().fromAssistant).isFalse(); 179 assertThat(smartReplyState.getSuppressedActions()).isNull(); 180 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 181 } 182 183 @Test chooseSmartRepliesAndActions_sysGeneratedSmartReplies()184 public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() { 185 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 186 // replies. 187 setupAppGeneratedReplies(null /* smartReplies */); 188 189 modifyRanking(mEntry) 190 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 191 .build(); 192 193 InflatedSmartReplyState smartReplyState = 194 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 195 196 assertThat(smartReplyState.getSmartReplies().choices) 197 .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder(); 198 assertThat(smartReplyState.getSmartReplies().fromAssistant).isTrue(); 199 assertThat(smartReplyState.getSmartActions()).isNull(); 200 assertThat(smartReplyState.getSuppressedActions()).isNull(); 201 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 202 } 203 204 @Test chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed()205 public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() { 206 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 207 // replies. 208 setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */); 209 210 NotificationEntryHelper.modifyRanking(mEntry) 211 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 212 .build(); 213 InflatedSmartReplyState smartReplyState = 214 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 215 216 assertThat(smartReplyState.getSmartReplies()).isNull(); 217 assertThat(smartReplyState.getSmartActions()).isNull(); 218 assertThat(smartReplyState.getSuppressedActions()).isNull(); 219 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 220 } 221 222 @Test chooseSmartRepliesAndActions_sysGeneratedSmartActions()223 public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() { 224 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 225 // actions. 226 setupAppGeneratedReplies(null /* smartReplies */); 227 228 modifyRanking(mEntry) 229 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) 230 .build(); 231 232 InflatedSmartReplyState smartReplyState = 233 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 234 235 assertThat(smartReplyState.getSmartReplies()).isNull(); 236 assertThat(smartReplyState.getSmartActions().actions) 237 .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder(); 238 assertThat(smartReplyState.getSmartActions().fromAssistant).isTrue(); 239 assertThat(smartReplyState.getSuppressedActions()).isNull(); 240 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 241 } 242 243 @Test chooseSmartRepliesAndActions_sysGeneratedPhishingSmartAction()244 public void chooseSmartRepliesAndActions_sysGeneratedPhishingSmartAction() { 245 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 246 // actions. 247 setupAppGeneratedReplies(null /* smartReplies */); 248 249 mNotification.actions = new Notification.Action[]{ 250 createAction("Details"), 251 createActionBuilder("Reply").addRemoteInput( 252 new RemoteInput.Builder("key").build()).build() 253 }; 254 255 modifyRanking(mEntry) 256 .setSmartActions( 257 createAction("Sys Smart Action 1"), 258 createActionBuilder("Sys Smart Action 2") 259 .setContextual(true) 260 .setSemanticAction(Notification.Action 261 .SEMANTIC_ACTION_CONVERSATION_IS_PHISHING) 262 .build()) 263 .build(); 264 265 InflatedSmartReplyState smartReplyState = 266 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 267 268 assertThat(smartReplyState.getSmartReplies()).isNull(); 269 assertThat(smartReplyState.getSmartActions().actions) 270 .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder(); 271 assertThat(smartReplyState.getSmartActions().fromAssistant).isTrue(); 272 assertThat(smartReplyState.getSuppressedActions()).isNotNull(); 273 assertThat(smartReplyState.getSuppressedActions().getSuppressedActionIndices()) 274 .containsExactly(1); 275 assertThat(smartReplyState.getHasPhishingAction()).isTrue(); 276 } 277 278 @Test chooseSmartRepliesAndActions_appGenPreferredOverSysGen()279 public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() { 280 CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"}; 281 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 282 // replies. 283 List<Notification.Action> appGenSmartActions = 284 createActions("Test Action 1", "Test Action 2"); 285 setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions); 286 287 modifyRanking(mEntry) 288 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 289 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) 290 .build(); 291 292 InflatedSmartReplyState smartReplyState = 293 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 294 295 assertThat(smartReplyState.getSmartReplies().choices) 296 .containsExactlyElementsIn(Arrays.asList(appGenSmartReplies)).inOrder(); 297 assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse(); 298 assertThat(smartReplyState.getSmartActions().actions) 299 .containsExactlyElementsIn(appGenSmartActions).inOrder(); 300 assertThat(smartReplyState.getSmartActions().fromAssistant).isFalse(); 301 assertThat(smartReplyState.getSuppressedActions()).isNull(); 302 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 303 } 304 305 @Test chooseSmartRepliesAndActions_disallowSysGenSmartActions()306 public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() { 307 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 308 // actions. 309 setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */); 310 when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false); 311 312 modifyRanking(mEntry) 313 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 314 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) 315 .build(); 316 317 InflatedSmartReplyState smartReplyState = 318 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 319 320 assertThat(smartReplyState.getSmartActions()).isNull(); 321 assertThat(smartReplyState.getSmartReplies()).isNull(); 322 assertThat(smartReplyState.getSuppressedActions()).isNull(); 323 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 324 } 325 326 @Test chooseSmartRepliesAndActions_lockTaskKioskModeEnabled_smartRepliesUnaffected()327 public void chooseSmartRepliesAndActions_lockTaskKioskModeEnabled_smartRepliesUnaffected() { 328 when(mActivityManagerWrapper.isLockTaskKioskModeActive()).thenReturn(true); 329 // No apps are white-listed 330 when(mDevicePolicyManagerWrapper.isLockTaskPermitted(anyString())).thenReturn(false); 331 332 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 333 // suggestions. 334 setupAppGeneratedReplies(null /* smartReplies */); 335 336 modifyRanking(mEntry) 337 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 338 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) 339 .build(); 340 341 InflatedSmartReplyState smartReplyState = 342 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 343 344 assertThat(smartReplyState.getSmartReplies().choices) 345 .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder(); 346 // Since no apps are allowlisted no actions should be shown. 347 assertThat(smartReplyState.getSmartActions().actions).isEmpty(); 348 assertThat(smartReplyState.getSuppressedActions()).isNull(); 349 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 350 } 351 352 @Test chooseSmartRepliesAndActions_lockTaskKioskModeEnabled_smartActionsAffected()353 public void chooseSmartRepliesAndActions_lockTaskKioskModeEnabled_smartActionsAffected() { 354 when(mActivityManagerWrapper.isLockTaskKioskModeActive()).thenReturn(true); 355 String allowedPackage = "allowedPackage"; 356 ResolveInfo allowedResolveInfo = new ResolveInfo(); 357 allowedResolveInfo.activityInfo = new ActivityInfo(); 358 allowedResolveInfo.activityInfo.packageName = allowedPackage; 359 when(mPackageManagerWrapper 360 .resolveActivity( 361 argThat(intent -> ALLOWLISTED_TEST_INTENT.getAction().equals( 362 intent.getAction())), 363 anyInt() /* flags */)) 364 .thenReturn(allowedResolveInfo); 365 when(mDevicePolicyManagerWrapper.isLockTaskPermitted(allowedPackage)).thenReturn(true); 366 367 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 368 // suggestions. 369 setupAppGeneratedReplies(null /* smartReplies */); 370 ArrayList<Notification.Action> actions = new ArrayList<>(); 371 actions.add(createAction("allowed action", ALLOWLISTED_TEST_INTENT)); 372 actions.add(createAction("non-allowed action", TEST_INTENT)); 373 374 modifyRanking(mEntry) 375 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 376 .setSmartActions(actions) 377 .build(); 378 379 InflatedSmartReplyState smartReplyState = 380 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 381 382 // Only the action for the allowlisted package should be allowed. 383 assertThat(smartReplyState.getSmartActions().actions) 384 .containsExactly(mEntry.getSmartActions().get(0)); 385 assertThat(smartReplyState.getSuppressedActions()).isNull(); 386 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 387 } 388 389 @Test chooseSmartRepliesAndActions_screenPinningModeEnabled_suggestionsUnaffected()390 public void chooseSmartRepliesAndActions_screenPinningModeEnabled_suggestionsUnaffected() { 391 when(mActivityManagerWrapper.isLockToAppActive()).thenReturn(true); 392 // No apps are white-listed 393 when(mDevicePolicyManagerWrapper.isLockTaskPermitted(anyString())).thenReturn(false); 394 395 // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart 396 // suggestions. 397 setupAppGeneratedReplies(null /* smartReplies */); 398 modifyRanking(mEntry) 399 .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) 400 .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) 401 .build(); 402 403 InflatedSmartReplyState smartReplyState = 404 mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); 405 406 // We don't restrict replies or actions in screen pinning mode. 407 assertThat(smartReplyState.getSmartReplies().choices) 408 .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder(); 409 assertThat(smartReplyState.getSmartActions().actions) 410 .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder(); 411 assertThat(smartReplyState.getSuppressedActions()).isNull(); 412 assertThat(smartReplyState.getHasPhishingAction()).isFalse(); 413 } 414 415 @Test areSuggestionsSimilar_trueForSimilar()416 public void areSuggestionsSimilar_trueForSimilar() { 417 List<CharSequence> leftReplies = createReplies("first reply", "second reply"); 418 List<CharSequence> rightReplies = createReplies("first reply", "second reply"); 419 List<Notification.Action> leftActions = Arrays.asList( 420 createAction("firstAction"), 421 createAction("secondAction")); 422 List<Notification.Action> rightActions = Arrays.asList( 423 createAction("firstAction"), 424 createAction("secondAction")); 425 List<Integer> leftSuppressed = Arrays.asList(1, 2); 426 List<Integer> rightSuppressed = Arrays.asList(1, 2); 427 boolean leftPhishing = true; 428 boolean rightPhishing = true; 429 430 InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( 431 new SmartReplies(leftReplies, null, null, false /* fromAssistant */), 432 new SmartActions(leftActions, false /* fromAssistant */), 433 new SuppressedActions(leftSuppressed), 434 leftPhishing); 435 InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( 436 new SmartReplies(rightReplies, null, null, false /* fromAssistant */), 437 new SmartActions(rightActions, false /* fromAssistant */), 438 new SuppressedActions(rightSuppressed), 439 rightPhishing); 440 441 assertThat(SmartReplyStateInflaterKt 442 .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) 443 .isTrue(); 444 } 445 446 @Test areSuggestionsSimilar_falseForDifferentReplies()447 public void areSuggestionsSimilar_falseForDifferentReplies() { 448 List<CharSequence> leftReplies = createReplies("first reply"); 449 List<CharSequence> rightReplies = createReplies("first reply", "second reply"); 450 List<Notification.Action> leftActions = Arrays.asList( 451 createAction("firstAction"), 452 createAction("secondAction")); 453 List<Notification.Action> rightActions = Arrays.asList( 454 createAction("firstAction"), 455 createAction("secondAction")); 456 List<Integer> leftSuppressed = Arrays.asList(1, 2); 457 List<Integer> rightSuppressed = Arrays.asList(1, 2); 458 boolean leftPhishing = true; 459 boolean rightPhishing = true; 460 461 InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( 462 new SmartReplies(leftReplies, null, null, false /* fromAssistant */), 463 new SmartActions(leftActions, false /* fromAssistant */), 464 new SuppressedActions(leftSuppressed), 465 leftPhishing); 466 InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( 467 new SmartReplies(rightReplies, null, null, false /* fromAssistant */), 468 new SmartActions(rightActions, false /* fromAssistant */), 469 new SuppressedActions(rightSuppressed), 470 rightPhishing); 471 472 assertThat(SmartReplyStateInflaterKt 473 .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) 474 .isFalse(); 475 } 476 477 @Test areSuggestionsSimilar_falseForDifferentActions()478 public void areSuggestionsSimilar_falseForDifferentActions() { 479 List<CharSequence> leftReplies = createReplies("first reply", "second reply"); 480 List<CharSequence> rightReplies = createReplies("first reply", "second reply"); 481 List<Notification.Action> leftActions = Arrays.asList( 482 createAction("firstAction"), 483 createAction("secondAction")); 484 List<Notification.Action> rightActions = Arrays.asList( 485 createAction("firstAction"), 486 createAction("not secondAction")); 487 List<Integer> leftSuppressed = Arrays.asList(1, 2); 488 List<Integer> rightSuppressed = Arrays.asList(1, 2); 489 boolean leftPhishing = true; 490 boolean rightPhishing = true; 491 492 InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( 493 new SmartReplies(leftReplies, null, null, false /* fromAssistant */), 494 new SmartActions(leftActions, false /* fromAssistant */), 495 new SuppressedActions(leftSuppressed), 496 leftPhishing); 497 InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( 498 new SmartReplies(rightReplies, null, null, false /* fromAssistant */), 499 new SmartActions(rightActions, false /* fromAssistant */), 500 new SuppressedActions(rightSuppressed), 501 rightPhishing); 502 503 assertThat(SmartReplyStateInflaterKt 504 .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) 505 .isFalse(); 506 } 507 508 @Test areSuggestionsSimilar_falseForDifferentSuppressedActions()509 public void areSuggestionsSimilar_falseForDifferentSuppressedActions() { 510 List<CharSequence> leftReplies = createReplies("first reply", "second reply"); 511 List<CharSequence> rightReplies = createReplies("first reply", "second reply"); 512 List<Notification.Action> leftActions = Arrays.asList( 513 createAction("firstAction"), 514 createAction("secondAction")); 515 List<Notification.Action> rightActions = Arrays.asList( 516 createAction("firstAction"), 517 createAction("secondAction")); 518 List<Integer> leftSuppressed = Arrays.asList(1, 2); 519 List<Integer> rightSuppressed = Arrays.asList(1, 3); 520 boolean leftPhishing = true; 521 boolean rightPhishing = true; 522 523 InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( 524 new SmartReplies(leftReplies, null, null, false /* fromAssistant */), 525 new SmartActions(leftActions, false /* fromAssistant */), 526 new SuppressedActions(leftSuppressed), 527 leftPhishing); 528 InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( 529 new SmartReplies(rightReplies, null, null, false /* fromAssistant */), 530 new SmartActions(rightActions, false /* fromAssistant */), 531 new SuppressedActions(rightSuppressed), 532 rightPhishing); 533 534 assertThat(SmartReplyStateInflaterKt 535 .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) 536 .isFalse(); 537 } 538 539 @Test areSuggestionsSimilar_falseForDifferentPhishing()540 public void areSuggestionsSimilar_falseForDifferentPhishing() { 541 List<CharSequence> leftReplies = createReplies("first reply", "second reply"); 542 List<CharSequence> rightReplies = createReplies("first reply", "second reply"); 543 List<Notification.Action> leftActions = Arrays.asList( 544 createAction("firstAction"), 545 createAction("secondAction")); 546 List<Notification.Action> rightActions = Arrays.asList( 547 createAction("firstAction"), 548 createAction("secondAction")); 549 List<Integer> leftSuppressed = Arrays.asList(1, 2); 550 List<Integer> rightSuppressed = Arrays.asList(1, 2); 551 boolean leftPhishing = true; 552 boolean rightPhishing = false; 553 554 InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( 555 new SmartReplies(leftReplies, null, null, false /* fromAssistant */), 556 new SmartActions(leftActions, false /* fromAssistant */), 557 new SuppressedActions(leftSuppressed), 558 leftPhishing); 559 InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( 560 new SmartReplies(rightReplies, null, null, false /* fromAssistant */), 561 new SmartActions(rightActions, false /* fromAssistant */), 562 new SuppressedActions(rightSuppressed), 563 rightPhishing); 564 565 assertThat(SmartReplyStateInflaterKt 566 .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) 567 .isFalse(); 568 } 569 setupAppGeneratedReplies(CharSequence[] smartReplies)570 private void setupAppGeneratedReplies(CharSequence[] smartReplies) { 571 setupAppGeneratedReplies(smartReplies, true /* allowSystemGeneratedReplies */); 572 } 573 setupAppGeneratedReplies( CharSequence[] smartReplies, boolean allowSystemGeneratedReplies)574 private void setupAppGeneratedReplies( 575 CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) { 576 PendingIntent pendingIntent = 577 PendingIntent.getBroadcast(mContext, 0, 578 TEST_INTENT.setPackage(mContext.getPackageName()), 579 PendingIntent.FLAG_MUTABLE); 580 Notification.Action action = 581 new Notification.Action.Builder(null, "Test Action", pendingIntent).build(); 582 when(mRemoteInput.getChoices()).thenReturn(smartReplies); 583 Pair<RemoteInput, Notification.Action> remoteInputActionPair = 584 Pair.create(mRemoteInput, action); 585 when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair); 586 587 Notification.Action freeFormRemoteInputAction = 588 createActionBuilder("Freeform Test Action") 589 .setAllowGeneratedReplies(allowSystemGeneratedReplies) 590 .build(); 591 Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair = 592 Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction); 593 when(mNotification.findRemoteInputActionPair(true)).thenReturn( 594 freeFormRemoteInputActionPair); 595 596 when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false); 597 } 598 setupAppGeneratedSuggestions( CharSequence[] smartReplies, List<Notification.Action> smartActions)599 private void setupAppGeneratedSuggestions( 600 CharSequence[] smartReplies, List<Notification.Action> smartActions) { 601 setupAppGeneratedReplies(smartReplies); 602 when(mNotification.getContextualActions()).thenReturn(smartActions); 603 } 604 createActionBuilder(String actionTitle)605 private Notification.Action.Builder createActionBuilder(String actionTitle) { 606 return createActionBuilder(actionTitle, TEST_INTENT); 607 } 608 createActionBuilder(String actionTitle, Intent intent)609 private Notification.Action.Builder createActionBuilder(String actionTitle, Intent intent) { 610 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, 611 intent.setPackage(mContext.getPackageName()), 612 PendingIntent.FLAG_MUTABLE); 613 return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent); 614 } 615 createAction(String actionTitle)616 private Notification.Action createAction(String actionTitle) { 617 return createActionBuilder(actionTitle).build(); 618 } 619 createAction(String actionTitle, Intent intent)620 private Notification.Action createAction(String actionTitle, Intent intent) { 621 return createActionBuilder(actionTitle, intent).build(); 622 } 623 createActions(String... actionTitles)624 private ArrayList<Notification.Action> createActions(String... actionTitles) { 625 ArrayList<Notification.Action> actions = new ArrayList<>(); 626 for (String title : actionTitles) { 627 actions.add(createAction(title)); 628 } 629 return actions; 630 } 631 createReplies(CharSequence... replies)632 private ArrayList<CharSequence> createReplies(CharSequence... replies) { 633 return new ArrayList<>(Arrays.asList(replies)); 634 } 635 } 636