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