1 /* 2 * Copyright (C) 2016 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 17 package com.android.intentresolver; 18 19 import static androidx.test.espresso.Espresso.onView; 20 import static androidx.test.espresso.action.ViewActions.click; 21 import static androidx.test.espresso.action.ViewActions.swipeUp; 22 import static androidx.test.espresso.assertion.ViewAssertions.matches; 23 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; 24 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; 25 import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; 26 import static androidx.test.espresso.matcher.ViewMatchers.withId; 27 import static androidx.test.espresso.matcher.ViewMatchers.withText; 28 29 import static com.android.intentresolver.MatcherUtils.first; 30 import static com.android.intentresolver.ResolverWrapperActivity.sOverrides; 31 32 import static org.hamcrest.CoreMatchers.allOf; 33 import static org.hamcrest.CoreMatchers.is; 34 import static org.hamcrest.CoreMatchers.not; 35 import static org.hamcrest.MatcherAssert.assertThat; 36 import static org.mockito.ArgumentMatchers.eq; 37 import static org.mockito.Mockito.when; 38 import static org.testng.Assert.assertFalse; 39 import static org.testng.Assert.fail; 40 41 import android.content.Intent; 42 import android.content.pm.ResolveInfo; 43 import android.net.Uri; 44 import android.os.RemoteException; 45 import android.os.UserHandle; 46 import android.text.TextUtils; 47 import android.view.View; 48 import android.widget.RelativeLayout; 49 import android.widget.TextView; 50 51 import androidx.test.InstrumentationRegistry; 52 import androidx.test.espresso.Espresso; 53 import androidx.test.espresso.NoMatchingViewException; 54 import androidx.test.rule.ActivityTestRule; 55 import androidx.test.runner.AndroidJUnit4; 56 57 import com.android.intentresolver.widget.ResolverDrawerLayout; 58 import com.android.internal.R; 59 60 import org.junit.Before; 61 import org.junit.Ignore; 62 import org.junit.Rule; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 import org.mockito.Mockito; 66 67 import java.util.ArrayList; 68 import java.util.List; 69 70 /** 71 * Resolver activity instrumentation tests 72 */ 73 @RunWith(AndroidJUnit4.class) 74 public class ResolverActivityTest { getConcreteIntentForLaunch(Intent clientIntent)75 protected Intent getConcreteIntentForLaunch(Intent clientIntent) { 76 clientIntent.setClass( 77 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getTargetContext(), 78 ResolverWrapperActivity.class); 79 return clientIntent; 80 } 81 82 @Rule 83 public ActivityTestRule<ResolverWrapperActivity> mActivityRule = 84 new ActivityTestRule<>(ResolverWrapperActivity.class, false, false); 85 86 @Before setup()87 public void setup() { 88 // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the 89 // permissions we require (which we'll read from the manifest at runtime). 90 androidx.test.platform.app.InstrumentationRegistry 91 .getInstrumentation() 92 .getUiAutomation() 93 .adoptShellPermissionIdentity(); 94 95 sOverrides.reset(); 96 } 97 98 @Test twoOptionsAndUserSelectsOne()99 public void twoOptionsAndUserSelectsOne() throws InterruptedException { 100 Intent sendIntent = createSendImageIntent(); 101 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 102 103 setupResolverControllers(resolvedComponentInfos); 104 105 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 106 Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); 107 waitForIdle(); 108 109 assertThat(activity.getAdapter().getCount(), is(2)); 110 111 ResolveInfo[] chosen = new ResolveInfo[1]; 112 sOverrides.onSafelyStartCallback = targetInfo -> { 113 chosen[0] = targetInfo.getResolveInfo(); 114 return true; 115 }; 116 117 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0); 118 onView(withText(toChoose.activityInfo.name)) 119 .perform(click()); 120 onView(withId(R.id.button_once)) 121 .perform(click()); 122 waitForIdle(); 123 assertThat(chosen[0], is(toChoose)); 124 } 125 126 @Ignore // Failing - b/144929805 127 @Test setMaxHeight()128 public void setMaxHeight() throws Exception { 129 Intent sendIntent = createSendImageIntent(); 130 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 131 132 setupResolverControllers(resolvedComponentInfos); 133 waitForIdle(); 134 135 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 136 final View viewPager = activity.findViewById(R.id.profile_pager); 137 final int initialResolverHeight = viewPager.getHeight(); 138 139 activity.runOnUiThread(() -> { 140 ResolverDrawerLayout layout = (ResolverDrawerLayout) 141 activity.findViewById( 142 R.id.contentPanel); 143 ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight 144 = initialResolverHeight - 1; 145 // Force a relayout 146 layout.invalidate(); 147 layout.requestLayout(); 148 }); 149 waitForIdle(); 150 assertThat("Drawer should be capped at maxHeight", 151 viewPager.getHeight() == (initialResolverHeight - 1)); 152 153 activity.runOnUiThread(() -> { 154 ResolverDrawerLayout layout = (ResolverDrawerLayout) 155 activity.findViewById( 156 R.id.contentPanel); 157 ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight 158 = initialResolverHeight + 1; 159 // Force a relayout 160 layout.invalidate(); 161 layout.requestLayout(); 162 }); 163 waitForIdle(); 164 assertThat("Drawer should not change height if its height is less than maxHeight", 165 viewPager.getHeight() == initialResolverHeight); 166 } 167 168 @Ignore // Failing - b/144929805 169 @Test setShowAtTopToTrue()170 public void setShowAtTopToTrue() throws Exception { 171 Intent sendIntent = createSendImageIntent(); 172 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 173 174 setupResolverControllers(resolvedComponentInfos); 175 waitForIdle(); 176 177 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 178 final View viewPager = activity.findViewById(R.id.profile_pager); 179 final View divider = activity.findViewById(R.id.divider); 180 final RelativeLayout profileView = 181 (RelativeLayout) activity.findViewById(R.id.profile_button).getParent(); 182 assertThat("Drawer should show at bottom by default", 183 profileView.getBottom() + divider.getHeight() == viewPager.getTop() 184 && profileView.getTop() > 0); 185 186 activity.runOnUiThread(() -> { 187 ResolverDrawerLayout layout = (ResolverDrawerLayout) 188 activity.findViewById( 189 R.id.contentPanel); 190 layout.setShowAtTop(true); 191 }); 192 waitForIdle(); 193 assertThat("Drawer should show at top with new attribute", 194 profileView.getBottom() + divider.getHeight() == viewPager.getTop() 195 && profileView.getTop() == 0); 196 } 197 198 @Test hasLastChosenActivity()199 public void hasLastChosenActivity() throws Exception { 200 Intent sendIntent = createSendImageIntent(); 201 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 202 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0); 203 204 setupResolverControllers(resolvedComponentInfos); 205 when(sOverrides.resolverListController.getLastChosen()) 206 .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0)); 207 208 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 209 waitForIdle(); 210 211 // The other entry is filtered to the last used slot 212 assertThat(activity.getAdapter().getCount(), is(1)); 213 assertThat(activity.getAdapter().getPlaceholderCount(), is(1)); 214 215 ResolveInfo[] chosen = new ResolveInfo[1]; 216 sOverrides.onSafelyStartCallback = targetInfo -> { 217 chosen[0] = targetInfo.getResolveInfo(); 218 return true; 219 }; 220 221 onView(withId(R.id.button_once)).perform(click()); 222 waitForIdle(); 223 assertThat(chosen[0], is(toChoose)); 224 } 225 226 @Test hasOtherProfileOneOption()227 public void hasOtherProfileOneOption() throws Exception { 228 List<ResolvedComponentInfo> personalResolvedComponentInfos = 229 createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10); 230 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 231 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 232 markWorkProfileUserAvailable(); 233 234 ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0); 235 Intent sendIntent = createSendImageIntent(); 236 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 237 Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); 238 waitForIdle(); 239 240 // The other entry is filtered to the last used slot 241 assertThat(activity.getAdapter().getCount(), is(1)); 242 243 ResolveInfo[] chosen = new ResolveInfo[1]; 244 sOverrides.onSafelyStartCallback = targetInfo -> { 245 chosen[0] = targetInfo.getResolveInfo(); 246 return true; 247 }; 248 // Make a stable copy of the components as the original list may be modified 249 List<ResolvedComponentInfo> stableCopy = 250 createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10); 251 // We pick the first one as there is another one in the work profile side 252 onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))) 253 .perform(click()); 254 onView(withId(R.id.button_once)) 255 .perform(click()); 256 waitForIdle(); 257 assertThat(chosen[0], is(toChoose)); 258 } 259 260 @Test hasOtherProfileTwoOptionsAndUserSelectsOne()261 public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception { 262 Intent sendIntent = createSendImageIntent(); 263 List<ResolvedComponentInfo> resolvedComponentInfos = 264 createResolvedComponentsForTestWithOtherProfile(3); 265 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 266 267 setupResolverControllers(resolvedComponentInfos); 268 269 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 270 Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); 271 waitForIdle(); 272 273 // The other entry is filtered to the other profile slot 274 assertThat(activity.getAdapter().getCount(), is(2)); 275 276 ResolveInfo[] chosen = new ResolveInfo[1]; 277 sOverrides.onSafelyStartCallback = targetInfo -> { 278 chosen[0] = targetInfo.getResolveInfo(); 279 return true; 280 }; 281 282 // Confirm that the button bar is disabled by default 283 onView(withId(R.id.button_once)).check(matches(not(isEnabled()))); 284 285 // Make a stable copy of the components as the original list may be modified 286 List<ResolvedComponentInfo> stableCopy = 287 createResolvedComponentsForTestWithOtherProfile(2); 288 289 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 290 .perform(click()); 291 onView(withId(R.id.button_once)).perform(click()); 292 waitForIdle(); 293 assertThat(chosen[0], is(toChoose)); 294 } 295 296 297 @Test hasLastChosenActivityAndOtherProfile()298 public void hasLastChosenActivityAndOtherProfile() throws Exception { 299 // In this case we prefer the other profile and don't display anything about the last 300 // chosen activity. 301 Intent sendIntent = createSendImageIntent(); 302 List<ResolvedComponentInfo> resolvedComponentInfos = 303 createResolvedComponentsForTestWithOtherProfile(3); 304 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 305 306 setupResolverControllers(resolvedComponentInfos); 307 when(sOverrides.resolverListController.getLastChosen()) 308 .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0)); 309 310 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 311 Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); 312 waitForIdle(); 313 314 // The other entry is filtered to the other profile slot 315 assertThat(activity.getAdapter().getCount(), is(2)); 316 317 ResolveInfo[] chosen = new ResolveInfo[1]; 318 sOverrides.onSafelyStartCallback = targetInfo -> { 319 chosen[0] = targetInfo.getResolveInfo(); 320 return true; 321 }; 322 323 // Confirm that the button bar is disabled by default 324 onView(withId(R.id.button_once)).check(matches(not(isEnabled()))); 325 326 // Make a stable copy of the components as the original list may be modified 327 List<ResolvedComponentInfo> stableCopy = 328 createResolvedComponentsForTestWithOtherProfile(2); 329 330 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 331 .perform(click()); 332 onView(withId(R.id.button_once)).perform(click()); 333 waitForIdle(); 334 assertThat(chosen[0], is(toChoose)); 335 } 336 337 @Test testWorkTab_displayedWhenWorkProfileUserAvailable()338 public void testWorkTab_displayedWhenWorkProfileUserAvailable() { 339 Intent sendIntent = createSendImageIntent(); 340 markWorkProfileUserAvailable(); 341 342 mActivityRule.launchActivity(sendIntent); 343 waitForIdle(); 344 345 onView(withId(R.id.tabs)).check(matches(isDisplayed())); 346 } 347 348 @Test testWorkTab_hiddenWhenWorkProfileUserNotAvailable()349 public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() { 350 Intent sendIntent = createSendImageIntent(); 351 352 mActivityRule.launchActivity(sendIntent); 353 waitForIdle(); 354 355 onView(withId(R.id.tabs)).check(matches(not(isDisplayed()))); 356 } 357 358 @Test testWorkTab_workTabListPopulatedBeforeGoingToTab()359 public void testWorkTab_workTabListPopulatedBeforeGoingToTab() throws InterruptedException { 360 List<ResolvedComponentInfo> personalResolvedComponentInfos = 361 createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10); 362 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 363 setupResolverControllers(personalResolvedComponentInfos, 364 new ArrayList<>(workResolvedComponentInfos)); 365 Intent sendIntent = createSendImageIntent(); 366 markWorkProfileUserAvailable(); 367 368 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 369 waitForIdle(); 370 371 assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0)); 372 // The work list adapter must be populated in advance before tapping the other tab 373 assertThat(activity.getWorkListAdapter().getCount(), is(4)); 374 } 375 376 @Test testWorkTab_workTabUsesExpectedAdapter()377 public void testWorkTab_workTabUsesExpectedAdapter() { 378 List<ResolvedComponentInfo> personalResolvedComponentInfos = 379 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 380 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 381 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 382 Intent sendIntent = createSendImageIntent(); 383 markWorkProfileUserAvailable(); 384 385 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 386 waitForIdle(); 387 onView(withText(R.string.resolver_work_tab)).perform(click()); 388 389 assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); 390 assertThat(activity.getWorkListAdapter().getCount(), is(4)); 391 } 392 393 @Test testWorkTab_personalTabUsesExpectedAdapter()394 public void testWorkTab_personalTabUsesExpectedAdapter() { 395 List<ResolvedComponentInfo> personalResolvedComponentInfos = 396 createResolvedComponentsForTestWithOtherProfile(3); 397 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 398 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 399 Intent sendIntent = createSendImageIntent(); 400 markWorkProfileUserAvailable(); 401 402 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 403 waitForIdle(); 404 onView(withText(R.string.resolver_work_tab)).perform(click()); 405 406 assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); 407 assertThat(activity.getPersonalListAdapter().getCount(), is(2)); 408 } 409 410 @Test testWorkTab_workProfileHasExpectedNumberOfTargets()411 public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException { 412 markWorkProfileUserAvailable(); 413 List<ResolvedComponentInfo> personalResolvedComponentInfos = 414 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 415 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 416 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 417 Intent sendIntent = createSendImageIntent(); 418 419 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 420 waitForIdle(); 421 422 onView(withText(R.string.resolver_work_tab)) 423 .perform(click()); 424 waitForIdle(); 425 assertThat(activity.getWorkListAdapter().getCount(), is(4)); 426 } 427 428 @Test testWorkTab_selectingWorkTabAppOpensAppInWorkProfile()429 public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException { 430 markWorkProfileUserAvailable(); 431 List<ResolvedComponentInfo> personalResolvedComponentInfos = 432 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 433 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 434 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 435 Intent sendIntent = createSendImageIntent(); 436 ResolveInfo[] chosen = new ResolveInfo[1]; 437 sOverrides.onSafelyStartCallback = targetInfo -> { 438 chosen[0] = targetInfo.getResolveInfo(); 439 return true; 440 }; 441 442 mActivityRule.launchActivity(sendIntent); 443 waitForIdle(); 444 onView(withText(R.string.resolver_work_tab)) 445 .perform(click()); 446 waitForIdle(); 447 onView(first(allOf(withText(workResolvedComponentInfos.get(0) 448 .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed()))) 449 .perform(click()); 450 onView(withId(R.id.button_once)) 451 .perform(click()); 452 453 waitForIdle(); 454 assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0))); 455 } 456 457 @Test testWorkTab_noPersonalApps_workTabHasExpectedNumberOfTargets()458 public void testWorkTab_noPersonalApps_workTabHasExpectedNumberOfTargets() 459 throws InterruptedException { 460 markWorkProfileUserAvailable(); 461 List<ResolvedComponentInfo> personalResolvedComponentInfos = 462 createResolvedComponentsForTestWithOtherProfile(1); 463 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 464 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 465 Intent sendIntent = createSendImageIntent(); 466 467 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 468 waitForIdle(); 469 onView(withText(R.string.resolver_work_tab)) 470 .perform(click()); 471 472 waitForIdle(); 473 assertThat(activity.getWorkListAdapter().getCount(), is(4)); 474 } 475 476 @Test testWorkTab_headerIsVisibleInPersonalTab()477 public void testWorkTab_headerIsVisibleInPersonalTab() { 478 markWorkProfileUserAvailable(); 479 List<ResolvedComponentInfo> personalResolvedComponentInfos = 480 createResolvedComponentsForTestWithOtherProfile(1); 481 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 482 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 483 Intent sendIntent = createOpenWebsiteIntent(); 484 485 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 486 waitForIdle(); 487 TextView headerText = activity.findViewById(R.id.title); 488 String initialText = headerText.getText().toString(); 489 assertFalse(initialText.isEmpty(), "Header text is empty."); 490 assertThat(headerText.getVisibility(), is(View.VISIBLE)); 491 } 492 493 @Test testWorkTab_switchTabs_headerStaysSame()494 public void testWorkTab_switchTabs_headerStaysSame() { 495 markWorkProfileUserAvailable(); 496 List<ResolvedComponentInfo> personalResolvedComponentInfos = 497 createResolvedComponentsForTestWithOtherProfile(1); 498 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 499 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 500 Intent sendIntent = createOpenWebsiteIntent(); 501 502 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 503 waitForIdle(); 504 TextView headerText = activity.findViewById(R.id.title); 505 String initialText = headerText.getText().toString(); 506 onView(withText(R.string.resolver_work_tab)) 507 .perform(click()); 508 509 waitForIdle(); 510 String currentText = headerText.getText().toString(); 511 assertThat(headerText.getVisibility(), is(View.VISIBLE)); 512 assertThat(String.format("Header text is not the same when switching tabs, personal profile" 513 + " header was %s but work profile header is %s", initialText, currentText), 514 TextUtils.equals(initialText, currentText)); 515 } 516 517 @Test testWorkTab_noPersonalApps_canStartWorkApps()518 public void testWorkTab_noPersonalApps_canStartWorkApps() 519 throws InterruptedException { 520 markWorkProfileUserAvailable(); 521 List<ResolvedComponentInfo> personalResolvedComponentInfos = 522 createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10); 523 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 524 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 525 Intent sendIntent = createSendImageIntent(); 526 ResolveInfo[] chosen = new ResolveInfo[1]; 527 sOverrides.onSafelyStartCallback = targetInfo -> { 528 chosen[0] = targetInfo.getResolveInfo(); 529 return true; 530 }; 531 532 mActivityRule.launchActivity(sendIntent); 533 waitForIdle(); 534 onView(withText(R.string.resolver_work_tab)) 535 .perform(click()); 536 waitForIdle(); 537 onView(first(allOf( 538 withText(workResolvedComponentInfos.get(0) 539 .getResolveInfoAt(0).activityInfo.applicationInfo.name), 540 isDisplayed()))) 541 .perform(click()); 542 onView(withId(R.id.button_once)) 543 .perform(click()); 544 waitForIdle(); 545 546 assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0))); 547 } 548 549 @Test testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown()550 public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() { 551 markWorkProfileUserAvailable(); 552 int workProfileTargets = 4; 553 List<ResolvedComponentInfo> personalResolvedComponentInfos = 554 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 555 List<ResolvedComponentInfo> workResolvedComponentInfos = 556 createResolvedComponentsForTest(workProfileTargets); 557 sOverrides.hasCrossProfileIntents = false; 558 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 559 Intent sendIntent = createSendImageIntent(); 560 sendIntent.setType("TestType"); 561 562 mActivityRule.launchActivity(sendIntent); 563 waitForIdle(); 564 onView(withText(R.string.resolver_work_tab)).perform(click()); 565 waitForIdle(); 566 onView(withId(R.id.contentPanel)) 567 .perform(swipeUp()); 568 569 onView(withText(R.string.resolver_cross_profile_blocked)) 570 .check(matches(isDisplayed())); 571 } 572 573 @Test testWorkTab_workProfileDisabled_emptyStateShown()574 public void testWorkTab_workProfileDisabled_emptyStateShown() { 575 markWorkProfileUserAvailable(); 576 int workProfileTargets = 4; 577 List<ResolvedComponentInfo> personalResolvedComponentInfos = 578 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 579 List<ResolvedComponentInfo> workResolvedComponentInfos = 580 createResolvedComponentsForTest(workProfileTargets); 581 sOverrides.isQuietModeEnabled = true; 582 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 583 Intent sendIntent = createSendImageIntent(); 584 sendIntent.setType("TestType"); 585 586 mActivityRule.launchActivity(sendIntent); 587 waitForIdle(); 588 onView(withId(R.id.contentPanel)) 589 .perform(swipeUp()); 590 onView(withText(R.string.resolver_work_tab)).perform(click()); 591 waitForIdle(); 592 593 onView(withText(R.string.resolver_turn_on_work_apps)) 594 .check(matches(isDisplayed())); 595 } 596 597 @Test testWorkTab_noWorkAppsAvailable_emptyStateShown()598 public void testWorkTab_noWorkAppsAvailable_emptyStateShown() { 599 markWorkProfileUserAvailable(); 600 List<ResolvedComponentInfo> personalResolvedComponentInfos = 601 createResolvedComponentsForTest(3); 602 List<ResolvedComponentInfo> workResolvedComponentInfos = 603 createResolvedComponentsForTest(0); 604 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 605 Intent sendIntent = createSendImageIntent(); 606 sendIntent.setType("TestType"); 607 608 mActivityRule.launchActivity(sendIntent); 609 waitForIdle(); 610 onView(withId(R.id.contentPanel)) 611 .perform(swipeUp()); 612 onView(withText(R.string.resolver_work_tab)).perform(click()); 613 waitForIdle(); 614 615 onView(withText(R.string.resolver_no_work_apps_available)) 616 .check(matches(isDisplayed())); 617 } 618 619 @Test testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown()620 public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() { 621 markWorkProfileUserAvailable(); 622 List<ResolvedComponentInfo> personalResolvedComponentInfos = 623 createResolvedComponentsForTest(3); 624 List<ResolvedComponentInfo> workResolvedComponentInfos = 625 createResolvedComponentsForTest(0); 626 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 627 Intent sendIntent = createSendImageIntent(); 628 sendIntent.setType("TestType"); 629 sOverrides.isQuietModeEnabled = true; 630 sOverrides.hasCrossProfileIntents = false; 631 632 mActivityRule.launchActivity(sendIntent); 633 waitForIdle(); 634 onView(withId(R.id.contentPanel)) 635 .perform(swipeUp()); 636 onView(withText(R.string.resolver_work_tab)).perform(click()); 637 waitForIdle(); 638 639 onView(withText(R.string.resolver_cross_profile_blocked)) 640 .check(matches(isDisplayed())); 641 } 642 643 @Test testMiniResolver()644 public void testMiniResolver() { 645 markWorkProfileUserAvailable(); 646 List<ResolvedComponentInfo> personalResolvedComponentInfos = 647 createResolvedComponentsForTest(1); 648 List<ResolvedComponentInfo> workResolvedComponentInfos = 649 createResolvedComponentsForTest(1); 650 // Personal profile only has a browser 651 personalResolvedComponentInfos.get(0).getResolveInfoAt(0).handleAllWebDataURI = true; 652 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 653 Intent sendIntent = createSendImageIntent(); 654 sendIntent.setType("TestType"); 655 656 mActivityRule.launchActivity(sendIntent); 657 waitForIdle(); 658 onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed())); 659 } 660 661 @Test testMiniResolver_noCurrentProfileTarget()662 public void testMiniResolver_noCurrentProfileTarget() { 663 markWorkProfileUserAvailable(); 664 List<ResolvedComponentInfo> personalResolvedComponentInfos = 665 createResolvedComponentsForTest(0); 666 List<ResolvedComponentInfo> workResolvedComponentInfos = 667 createResolvedComponentsForTest(1); 668 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 669 Intent sendIntent = createSendImageIntent(); 670 sendIntent.setType("TestType"); 671 672 mActivityRule.launchActivity(sendIntent); 673 waitForIdle(); 674 675 // Need to ensure mini resolver doesn't trigger here. 676 assertNotMiniResolver(); 677 } 678 assertNotMiniResolver()679 private void assertNotMiniResolver() { 680 try { 681 onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed())); 682 } catch (NoMatchingViewException e) { 683 return; 684 } 685 fail("Mini resolver present but shouldn't be"); 686 } 687 688 @Test testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown()689 public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() { 690 markWorkProfileUserAvailable(); 691 List<ResolvedComponentInfo> personalResolvedComponentInfos = 692 createResolvedComponentsForTest(3); 693 List<ResolvedComponentInfo> workResolvedComponentInfos = 694 createResolvedComponentsForTest(0); 695 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 696 Intent sendIntent = createSendImageIntent(); 697 sendIntent.setType("TestType"); 698 sOverrides.isQuietModeEnabled = true; 699 700 mActivityRule.launchActivity(sendIntent); 701 waitForIdle(); 702 onView(withId(R.id.contentPanel)) 703 .perform(swipeUp()); 704 onView(withText(R.string.resolver_work_tab)).perform(click()); 705 waitForIdle(); 706 707 onView(withText(R.string.resolver_no_work_apps_available)) 708 .check(matches(isDisplayed())); 709 } 710 711 @Test testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_autolaunch()712 public void testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_autolaunch() { 713 markWorkProfileUserAvailable(); 714 int workProfileTargets = 4; 715 List<ResolvedComponentInfo> personalResolvedComponentInfos = 716 createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10); 717 List<ResolvedComponentInfo> workResolvedComponentInfos = 718 createResolvedComponentsForTest(workProfileTargets); 719 sOverrides.hasCrossProfileIntents = false; 720 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 721 Intent sendIntent = createSendImageIntent(); 722 sendIntent.setType("TestType"); 723 ResolveInfo[] chosen = new ResolveInfo[1]; 724 sOverrides.onSafelyStartCallback = targetInfo -> { 725 chosen[0] = targetInfo.getResolveInfo(); 726 return true; 727 }; 728 729 mActivityRule.launchActivity(sendIntent); 730 waitForIdle(); 731 732 assertThat(chosen[0], is(personalResolvedComponentInfos.get(1).getResolveInfoAt(0))); 733 } 734 735 @Test testLayoutWithDefault_withWorkTab_neverShown()736 public void testLayoutWithDefault_withWorkTab_neverShown() throws RemoteException { 737 markWorkProfileUserAvailable(); 738 739 // In this case we prefer the other profile and don't display anything about the last 740 // chosen activity. 741 Intent sendIntent = createSendImageIntent(); 742 List<ResolvedComponentInfo> resolvedComponentInfos = 743 createResolvedComponentsForTest(2); 744 745 setupResolverControllers(resolvedComponentInfos); 746 when(sOverrides.resolverListController.getLastChosen()) 747 .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0)); 748 749 final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); 750 Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource()); 751 waitForIdle(); 752 753 // The other entry is filtered to the last used slot 754 assertThat(activity.getAdapter().hasFilteredItem(), is(false)); 755 assertThat(activity.getAdapter().getCount(), is(2)); 756 assertThat(activity.getAdapter().getPlaceholderCount(), is(2)); 757 } 758 createSendImageIntent()759 private Intent createSendImageIntent() { 760 Intent sendIntent = new Intent(); 761 sendIntent.setAction(Intent.ACTION_SEND); 762 sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending"); 763 sendIntent.setType("image/jpeg"); 764 return sendIntent; 765 } 766 createOpenWebsiteIntent()767 private Intent createOpenWebsiteIntent() { 768 Intent sendIntent = new Intent(); 769 sendIntent.setAction(Intent.ACTION_VIEW); 770 sendIntent.setData(Uri.parse("https://google.com")); 771 return sendIntent; 772 } 773 createResolvedComponentsForTest(int numberOfResults)774 private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) { 775 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 776 for (int i = 0; i < numberOfResults; i++) { 777 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); 778 } 779 return infoList; 780 } 781 createResolvedComponentsForTestWithOtherProfile( int numberOfResults)782 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( 783 int numberOfResults) { 784 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 785 for (int i = 0; i < numberOfResults; i++) { 786 if (i == 0) { 787 infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i)); 788 } else { 789 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); 790 } 791 } 792 return infoList; 793 } 794 createResolvedComponentsForTestWithOtherProfile( int numberOfResults, int userId)795 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( 796 int numberOfResults, int userId) { 797 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 798 for (int i = 0; i < numberOfResults; i++) { 799 if (i == 0) { 800 infoList.add( 801 ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId)); 802 } else { 803 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); 804 } 805 } 806 return infoList; 807 } 808 waitForIdle()809 private void waitForIdle() { 810 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 811 } 812 markWorkProfileUserAvailable()813 private void markWorkProfileUserAvailable() { 814 ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10); 815 } 816 setupResolverControllers( List<ResolvedComponentInfo> personalResolvedComponentInfos)817 private void setupResolverControllers( 818 List<ResolvedComponentInfo> personalResolvedComponentInfos) { 819 setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>()); 820 } 821 setupResolverControllers( List<ResolvedComponentInfo> personalResolvedComponentInfos, List<ResolvedComponentInfo> workResolvedComponentInfos)822 private void setupResolverControllers( 823 List<ResolvedComponentInfo> personalResolvedComponentInfos, 824 List<ResolvedComponentInfo> workResolvedComponentInfos) { 825 when(sOverrides.resolverListController.getResolversForIntentAsUser( 826 Mockito.anyBoolean(), 827 Mockito.anyBoolean(), 828 Mockito.anyBoolean(), 829 Mockito.isA(List.class), 830 eq(UserHandle.SYSTEM))) 831 .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); 832 when(sOverrides.workResolverListController.getResolversForIntentAsUser( 833 Mockito.anyBoolean(), 834 Mockito.anyBoolean(), 835 Mockito.anyBoolean(), 836 Mockito.isA(List.class), 837 eq(UserHandle.SYSTEM))) 838 .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); 839 when(sOverrides.workResolverListController.getResolversForIntentAsUser( 840 Mockito.anyBoolean(), 841 Mockito.anyBoolean(), 842 Mockito.anyBoolean(), 843 Mockito.isA(List.class), 844 eq(UserHandle.of(10)))) 845 .thenReturn(new ArrayList<>(workResolvedComponentInfos)); 846 } 847 } 848