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 android.app.Activity.RESULT_OK; 20 21 import static androidx.test.espresso.Espresso.onView; 22 import static androidx.test.espresso.action.ViewActions.click; 23 import static androidx.test.espresso.action.ViewActions.longClick; 24 import static androidx.test.espresso.action.ViewActions.swipeUp; 25 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; 26 import static androidx.test.espresso.assertion.ViewAssertions.matches; 27 import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; 28 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; 29 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; 30 import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; 31 import static androidx.test.espresso.matcher.ViewMatchers.withId; 32 import static androidx.test.espresso.matcher.ViewMatchers.withText; 33 34 import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_CHOOSER_TARGET; 35 import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_DEFAULT; 36 import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; 37 import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER; 38 import static com.android.intentresolver.ChooserListAdapter.CALLER_TARGET_SCORE_BOOST; 39 import static com.android.intentresolver.ChooserListAdapter.SHORTCUT_TARGET_SCORE_BOOST; 40 import static com.android.intentresolver.MatcherUtils.first; 41 42 import static com.google.common.truth.Truth.assertThat; 43 import static com.google.common.truth.Truth.assertWithMessage; 44 45 import static junit.framework.Assert.assertNull; 46 47 import static org.hamcrest.CoreMatchers.allOf; 48 import static org.hamcrest.CoreMatchers.is; 49 import static org.hamcrest.CoreMatchers.not; 50 import static org.hamcrest.MatcherAssert.assertThat; 51 import static org.junit.Assert.assertEquals; 52 import static org.junit.Assert.assertTrue; 53 import static org.mockito.ArgumentMatchers.any; 54 import static org.mockito.ArgumentMatchers.anyInt; 55 import static org.mockito.ArgumentMatchers.eq; 56 import static org.mockito.Mockito.doReturn; 57 import static org.mockito.Mockito.mock; 58 import static org.mockito.Mockito.times; 59 import static org.mockito.Mockito.verify; 60 import static org.mockito.Mockito.when; 61 62 import android.app.PendingIntent; 63 import android.app.usage.UsageStatsManager; 64 import android.content.BroadcastReceiver; 65 import android.content.ClipData; 66 import android.content.ClipDescription; 67 import android.content.ClipboardManager; 68 import android.content.ComponentName; 69 import android.content.Context; 70 import android.content.Intent; 71 import android.content.IntentFilter; 72 import android.content.pm.ActivityInfo; 73 import android.content.pm.ApplicationInfo; 74 import android.content.pm.PackageManager; 75 import android.content.pm.ResolveInfo; 76 import android.content.pm.ShortcutInfo; 77 import android.content.pm.ShortcutManager.ShareShortcutInfo; 78 import android.content.res.Configuration; 79 import android.content.res.Resources; 80 import android.database.Cursor; 81 import android.graphics.Bitmap; 82 import android.graphics.Canvas; 83 import android.graphics.Color; 84 import android.graphics.Paint; 85 import android.graphics.Rect; 86 import android.graphics.Typeface; 87 import android.graphics.drawable.Icon; 88 import android.net.Uri; 89 import android.os.Bundle; 90 import android.os.UserHandle; 91 import android.platform.test.flag.junit.CheckFlagsRule; 92 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 93 import android.provider.DeviceConfig; 94 import android.provider.Settings; 95 import android.service.chooser.ChooserAction; 96 import android.service.chooser.ChooserTarget; 97 import android.text.Spannable; 98 import android.text.SpannableStringBuilder; 99 import android.text.Spanned; 100 import android.text.style.BackgroundColorSpan; 101 import android.text.style.ForegroundColorSpan; 102 import android.text.style.StyleSpan; 103 import android.text.style.UnderlineSpan; 104 import android.util.Pair; 105 import android.util.SparseArray; 106 import android.view.View; 107 import android.view.WindowManager; 108 import android.widget.TextView; 109 110 import androidx.annotation.NonNull; 111 import androidx.annotation.Nullable; 112 import androidx.recyclerview.widget.GridLayoutManager; 113 import androidx.recyclerview.widget.RecyclerView; 114 import androidx.test.espresso.contrib.RecyclerViewActions; 115 import androidx.test.espresso.matcher.BoundedDiagnosingMatcher; 116 import androidx.test.espresso.matcher.ViewMatchers; 117 import androidx.test.platform.app.InstrumentationRegistry; 118 import androidx.test.rule.ActivityTestRule; 119 120 import com.android.intentresolver.chooser.DisplayResolveInfo; 121 import com.android.intentresolver.contentpreview.FakeThumbnailLoader; 122 import com.android.intentresolver.contentpreview.ImageLoader; 123 import com.android.intentresolver.contentpreview.ImageLoaderModule; 124 import com.android.intentresolver.contentpreview.PreviewCacheSize; 125 import com.android.intentresolver.contentpreview.PreviewMaxConcurrency; 126 import com.android.intentresolver.contentpreview.ThumbnailLoader; 127 import com.android.intentresolver.data.repository.FakeUserRepository; 128 import com.android.intentresolver.data.repository.UserRepository; 129 import com.android.intentresolver.data.repository.UserRepositoryModule; 130 import com.android.intentresolver.ext.RecyclerViewExt; 131 import com.android.intentresolver.inject.ApplicationUser; 132 import com.android.intentresolver.inject.PackageManagerModule; 133 import com.android.intentresolver.inject.ProfileParent; 134 import com.android.intentresolver.logging.EventLog; 135 import com.android.intentresolver.logging.FakeEventLog; 136 import com.android.intentresolver.platform.AppPredictionAvailable; 137 import com.android.intentresolver.platform.AppPredictionModule; 138 import com.android.intentresolver.platform.GlobalSettings; 139 import com.android.intentresolver.platform.ImageEditor; 140 import com.android.intentresolver.platform.ImageEditorModule; 141 import com.android.intentresolver.shared.model.User; 142 import com.android.intentresolver.shortcuts.ShortcutLoader; 143 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; 144 145 import dagger.hilt.android.qualifiers.ApplicationContext; 146 import dagger.hilt.android.testing.BindValue; 147 import dagger.hilt.android.testing.HiltAndroidRule; 148 import dagger.hilt.android.testing.HiltAndroidTest; 149 import dagger.hilt.android.testing.UninstallModules; 150 151 import org.hamcrest.Description; 152 import org.hamcrest.Matcher; 153 import org.hamcrest.Matchers; 154 import org.junit.Before; 155 import org.junit.Ignore; 156 import org.junit.Rule; 157 import org.junit.Test; 158 import org.junit.runner.RunWith; 159 import org.junit.runners.Parameterized; 160 import org.junit.runners.Parameterized.Parameters; 161 import org.mockito.ArgumentCaptor; 162 import org.mockito.Mockito; 163 164 import java.util.ArrayList; 165 import java.util.Arrays; 166 import java.util.HashMap; 167 import java.util.List; 168 import java.util.Map; 169 import java.util.Optional; 170 import java.util.concurrent.CountDownLatch; 171 import java.util.concurrent.TimeUnit; 172 import java.util.concurrent.atomic.AtomicReference; 173 import java.util.function.Consumer; 174 175 import javax.inject.Inject; 176 177 /** 178 * Instrumentation tests for ChooserActivity. 179 * <p> 180 * Legacy test suite migrated from framework CoreTests. 181 */ 182 @SuppressWarnings("OptionalUsedAsFieldOrParameterType") 183 @RunWith(Parameterized.class) 184 @HiltAndroidTest 185 @UninstallModules({ 186 AppPredictionModule.class, 187 ImageEditorModule.class, 188 PackageManagerModule.class, 189 ImageLoaderModule.class, 190 UserRepositoryModule.class, 191 }) 192 public class ChooserActivityTest { 193 getEventLog(ChooserWrapperActivity activity)194 private static FakeEventLog getEventLog(ChooserWrapperActivity activity) { 195 return (FakeEventLog) activity.mEventLog; 196 } 197 198 private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry 199 .getInstrumentation().getTargetContext().getUser(); 200 201 private static final User PERSONAL_USER = 202 new User(PERSONAL_USER_HANDLE.getIdentifier(), User.Role.PERSONAL); 203 204 private static final UserHandle WORK_PROFILE_USER_HANDLE = UserHandle.of(10); 205 206 private static final User WORK_USER = 207 new User(WORK_PROFILE_USER_HANDLE.getIdentifier(), User.Role.WORK); 208 209 private static final UserHandle CLONE_PROFILE_USER_HANDLE = UserHandle.of(11); 210 211 private static final User CLONE_USER = 212 new User(CLONE_PROFILE_USER_HANDLE.getIdentifier(), User.Role.CLONE); 213 214 @Parameters(name = "appPrediction={0}") parameters()215 public static Iterable<?> parameters() { 216 return Arrays.asList( 217 /* appPredictionAvailable = */ true, 218 /* appPredictionAvailable = */ false 219 ); 220 } 221 222 private static final String TEST_MIME_TYPE = "application/TestType"; 223 224 private static final int CONTENT_PREVIEW_IMAGE = 1; 225 private static final int CONTENT_PREVIEW_FILE = 2; 226 private static final int CONTENT_PREVIEW_TEXT = 3; 227 228 @Rule(order = 0) 229 public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 230 231 @Rule(order = 1) 232 public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this); 233 234 @Rule(order = 2) 235 public ActivityTestRule<ChooserWrapperActivity> mActivityRule = 236 new ActivityTestRule<>(ChooserWrapperActivity.class, false, false); 237 238 @Inject 239 @ApplicationContext 240 Context mContext; 241 242 @Inject 243 GlobalSettings mGlobalSettings; 244 245 /** An arbitrary pre-installed activity that handles this type of intent. */ 246 @BindValue 247 @ImageEditor 248 final Optional<ComponentName> mImageEditor = Optional.ofNullable( 249 ComponentName.unflattenFromString("com.google.android.apps.messaging/" 250 + ".ui.conversationlist.ShareIntentActivity")); 251 252 /** Whether an AppPredictionService is available for use. */ 253 @BindValue 254 @AppPredictionAvailable 255 final boolean mAppPredictionAvailable; 256 257 @BindValue 258 PackageManager mPackageManager; 259 260 /** "launchedAs" */ 261 @BindValue 262 @ApplicationUser 263 UserHandle mApplicationUser = PERSONAL_USER_HANDLE; 264 265 @BindValue 266 @ProfileParent 267 UserHandle mProfileParent = PERSONAL_USER_HANDLE; 268 269 private final FakeUserRepository mFakeUserRepo = new FakeUserRepository(List.of(PERSONAL_USER)); 270 271 @BindValue 272 final UserRepository mUserRepository = mFakeUserRepo; 273 274 private final FakeImageLoader mFakeImageLoader = new FakeImageLoader(); 275 276 @BindValue 277 final ImageLoader mImageLoader = mFakeImageLoader; 278 279 @BindValue 280 @PreviewCacheSize 281 int mPreviewCacheSize = 16; 282 283 @BindValue 284 @PreviewMaxConcurrency 285 int mPreviewMaxConcurrency = 4; 286 287 @BindValue 288 ThumbnailLoader mThumbnailLoader = new FakeThumbnailLoader(); 289 290 @Before setUp()291 public void setUp() { 292 // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the 293 // permissions we require (which we'll read from the manifest at runtime). 294 InstrumentationRegistry 295 .getInstrumentation() 296 .getUiAutomation() 297 .adoptShellPermissionIdentity(); 298 299 cleanOverrideData(); 300 301 // Assign @Inject fields 302 mHiltAndroidRule.inject(); 303 304 // Populate @BindValue dependencies using injected values. These fields contribute 305 // values to the dependency graph at activity launch time. This allows replacing 306 // arbitrary bindings per-test case if needed. 307 mPackageManager = mContext.getPackageManager(); 308 309 // TODO: inject image loader in the prod code and remove this override 310 ChooserActivityOverrideData.getInstance().imageLoader = mFakeImageLoader; 311 } 312 ChooserActivityTest(boolean appPredictionAvailable)313 public ChooserActivityTest(boolean appPredictionAvailable) { 314 mAppPredictionAvailable = appPredictionAvailable; 315 } 316 setDeviceConfigProperty( @onNull String propertyName, @NonNull String value)317 private void setDeviceConfigProperty( 318 @NonNull String propertyName, 319 @NonNull String value) { 320 // TODO: consider running with {@link #runWithShellPermissionIdentity()} to more narrowly 321 // request WRITE_DEVICE_CONFIG permissions if we get rid of the broad grant we currently 322 // configure in {@link #setup()}. 323 // TODO: is it really appropriate that this is always set with makeDefault=true? 324 boolean valueWasSet = DeviceConfig.setProperty( 325 DeviceConfig.NAMESPACE_SYSTEMUI, 326 propertyName, 327 value, 328 true /* makeDefault */); 329 if (!valueWasSet) { 330 throw new IllegalStateException( 331 "Could not set " + propertyName + " to " + value); 332 } 333 } 334 cleanOverrideData()335 public void cleanOverrideData() { 336 ChooserActivityOverrideData.getInstance().reset(); 337 338 setDeviceConfigProperty( 339 SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI, 340 Boolean.toString(true)); 341 } 342 createFakePackageManager(ResolveInfo resolveInfo)343 private static PackageManager createFakePackageManager(ResolveInfo resolveInfo) { 344 PackageManager packageManager = mock(PackageManager.class); 345 when(packageManager.resolveActivity(any(Intent.class), any())).thenReturn(resolveInfo); 346 return packageManager; 347 } 348 349 @Test customTitle()350 public void customTitle() throws InterruptedException { 351 Intent viewIntent = createViewTextIntent(); 352 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 353 354 setupResolverControllers(resolvedComponentInfos); 355 final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity( 356 Intent.createChooser(viewIntent, "chooser test")); 357 358 waitForIdle(); 359 assertThat(activity.getAdapter().getCount(), is(2)); 360 assertThat(activity.getAdapter().getServiceTargetCount(), is(0)); 361 onView(withId(android.R.id.title)).check(matches(withText("chooser test"))); 362 } 363 364 @Test customTitleIgnoredForSendIntents()365 public void customTitleIgnoredForSendIntents() throws InterruptedException { 366 Intent sendIntent = createSendTextIntent(); 367 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 368 369 setupResolverControllers(resolvedComponentInfos); 370 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test")); 371 waitForIdle(); 372 onView(withId(android.R.id.title)) 373 .check(matches(withText(R.string.whichSendApplication))); 374 } 375 376 @Test emptyTitle()377 public void emptyTitle() throws InterruptedException { 378 Intent sendIntent = createSendTextIntent(); 379 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 380 381 setupResolverControllers(resolvedComponentInfos); 382 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 383 waitForIdle(); 384 onView(withId(android.R.id.title)) 385 .check(matches(withText(R.string.whichSendApplication))); 386 } 387 388 @Test test_shareRichTextWithRichTitle_richTextAndRichTitleDisplayed()389 public void test_shareRichTextWithRichTitle_richTextAndRichTitleDisplayed() { 390 CharSequence title = new SpannableStringBuilder() 391 .append("Rich", new UnderlineSpan(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE) 392 .append( 393 "Title", 394 new ForegroundColorSpan(Color.RED), 395 Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 396 CharSequence sharedText = new SpannableStringBuilder() 397 .append( 398 "Rich", 399 new BackgroundColorSpan(Color.YELLOW), 400 Spanned.SPAN_INCLUSIVE_EXCLUSIVE) 401 .append( 402 "Text", 403 new StyleSpan(Typeface.ITALIC), 404 Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 405 Intent sendIntent = createSendTextIntent(); 406 sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText); 407 sendIntent.putExtra(Intent.EXTRA_TITLE, title); 408 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 409 setupResolverControllers(resolvedComponentInfos); 410 411 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 412 waitForIdle(); 413 414 onView(withId(com.android.internal.R.id.content_preview_title)) 415 .check((view, e) -> { 416 assertThat(view).isInstanceOf(TextView.class); 417 CharSequence text = ((TextView) view).getText(); 418 assertThat(text).isInstanceOf(Spanned.class); 419 Spanned spanned = (Spanned) text; 420 assertThat(spanned.getSpans(0, spanned.length(), Object.class)) 421 .hasLength(2); 422 assertThat(spanned.getSpans(0, 4, UnderlineSpan.class)).hasLength(1); 423 assertThat(spanned.getSpans(4, spanned.length(), ForegroundColorSpan.class)) 424 .hasLength(1); 425 }); 426 427 onView(withId(com.android.internal.R.id.content_preview_text)) 428 .check((view, e) -> { 429 assertThat(view).isInstanceOf(TextView.class); 430 CharSequence text = ((TextView) view).getText(); 431 assertThat(text).isInstanceOf(Spanned.class); 432 Spanned spanned = (Spanned) text; 433 assertThat(spanned.getSpans(0, spanned.length(), Object.class)) 434 .hasLength(2); 435 assertThat(spanned.getSpans(0, 4, BackgroundColorSpan.class)).hasLength(1); 436 assertThat(spanned.getSpans(4, spanned.length(), StyleSpan.class)).hasLength(1); 437 }); 438 } 439 440 @Test emptyPreviewTitleAndThumbnail()441 public void emptyPreviewTitleAndThumbnail() throws InterruptedException { 442 Intent sendIntent = createSendTextIntentWithPreview(null, null); 443 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 444 445 setupResolverControllers(resolvedComponentInfos); 446 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 447 waitForIdle(); 448 onView(withId(com.android.internal.R.id.content_preview_title)) 449 .check(matches(not(isDisplayed()))); 450 onView(withId(com.android.internal.R.id.content_preview_thumbnail)) 451 .check(matches(not(isDisplayed()))); 452 } 453 454 @Test visiblePreviewTitleWithoutThumbnail()455 public void visiblePreviewTitleWithoutThumbnail() throws InterruptedException { 456 String previewTitle = "My Content Preview Title"; 457 Intent sendIntent = createSendTextIntentWithPreview(previewTitle, null); 458 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 459 460 setupResolverControllers(resolvedComponentInfos); 461 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 462 waitForIdle(); 463 onView(withId(com.android.internal.R.id.content_preview_title)) 464 .check(matches(isDisplayed())); 465 onView(withId(com.android.internal.R.id.content_preview_title)) 466 .check(matches(withText(previewTitle))); 467 onView(withId(com.android.internal.R.id.content_preview_thumbnail)) 468 .check(matches(not(isDisplayed()))); 469 } 470 471 @Test visiblePreviewTitleWithInvalidThumbnail()472 public void visiblePreviewTitleWithInvalidThumbnail() throws InterruptedException { 473 String previewTitle = "My Content Preview Title"; 474 Intent sendIntent = createSendTextIntentWithPreview(previewTitle, 475 Uri.parse("tel:(+49)12345789")); 476 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 477 478 setupResolverControllers(resolvedComponentInfos); 479 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 480 waitForIdle(); 481 onView(withId(com.android.internal.R.id.content_preview_title)) 482 .check(matches(isDisplayed())); 483 onView(withId(com.android.internal.R.id.content_preview_thumbnail)) 484 .check(matches(not(isDisplayed()))); 485 } 486 487 @Test visiblePreviewTitleAndThumbnail()488 public void visiblePreviewTitleAndThumbnail() { 489 String previewTitle = "My Content Preview Title"; 490 Uri uri = Uri.parse( 491 "android.resource://com.android.frameworks.coretests/" 492 + com.android.intentresolver.tests.R.drawable.test320x240); 493 Intent sendIntent = createSendTextIntentWithPreview(previewTitle, uri); 494 mFakeImageLoader.setBitmap(uri, createBitmap()); 495 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 496 497 setupResolverControllers(resolvedComponentInfos); 498 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 499 waitForIdle(); 500 onView(withId(com.android.internal.R.id.content_preview_title)) 501 .check(matches(isDisplayed())); 502 onView(withId(com.android.internal.R.id.content_preview_thumbnail)) 503 .check(matches(isDisplayed())); 504 } 505 506 @Test @Ignore twoOptionsAndUserSelectsOne()507 public void twoOptionsAndUserSelectsOne() throws InterruptedException { 508 Intent sendIntent = createSendTextIntent(); 509 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 510 511 setupResolverControllers(resolvedComponentInfos); 512 513 final IChooserWrapper activity = (IChooserWrapper) 514 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 515 waitForIdle(); 516 517 assertThat(activity.getAdapter().getCount(), is(2)); 518 onView(withId(com.android.internal.R.id.profile_button)).check(doesNotExist()); 519 520 ResolveInfo[] chosen = new ResolveInfo[1]; 521 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 522 chosen[0] = targetInfo.getResolveInfo(); 523 return true; 524 }; 525 526 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0); 527 onView(withText(toChoose.activityInfo.name)) 528 .perform(click()); 529 waitForIdle(); 530 assertThat(chosen[0], is(toChoose)); 531 } 532 533 @Test @Ignore fourOptionsStackedIntoOneTarget()534 public void fourOptionsStackedIntoOneTarget() throws InterruptedException { 535 Intent sendIntent = createSendTextIntent(); 536 537 // create just enough targets to ensure the a-z list should be shown 538 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1); 539 540 // next create 4 targets in a single app that should be stacked into a single target 541 String packageName = "xxx.yyy"; 542 String appName = "aaa"; 543 ComponentName cn = new ComponentName(packageName, appName); 544 Intent intent = new Intent("fakeIntent"); 545 List<ResolvedComponentInfo> infosToStack = new ArrayList<>(); 546 for (int i = 0; i < 4; i++) { 547 ResolveInfo resolveInfo = ResolverDataProvider.createResolveInfo(i, 548 UserHandle.USER_CURRENT, PERSONAL_USER_HANDLE); 549 resolveInfo.activityInfo.applicationInfo.name = appName; 550 resolveInfo.activityInfo.applicationInfo.packageName = packageName; 551 resolveInfo.activityInfo.packageName = packageName; 552 resolveInfo.activityInfo.name = "ccc" + i; 553 infosToStack.add(new ResolvedComponentInfo(cn, intent, resolveInfo)); 554 } 555 resolvedComponentInfos.addAll(infosToStack); 556 557 setupResolverControllers(resolvedComponentInfos); 558 559 final IChooserWrapper activity = (IChooserWrapper) 560 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 561 waitForIdle(); 562 563 // expect 1 unique targets + 1 group + 4 ranked app targets 564 assertThat(activity.getAdapter().getCount(), is(6)); 565 566 ResolveInfo[] chosen = new ResolveInfo[1]; 567 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 568 chosen[0] = targetInfo.getResolveInfo(); 569 return true; 570 }; 571 572 onView(allOf(withText(appName), hasSibling(withText("")))).perform(click()); 573 waitForIdle(); 574 575 // clicking will launch a dialog to choose the activity within the app 576 onView(withText(appName)).check(matches(isDisplayed())); 577 int i = 0; 578 for (ResolvedComponentInfo rci: infosToStack) { 579 onView(withText("ccc" + i)).check(matches(isDisplayed())); 580 ++i; 581 } 582 } 583 584 @Test @Ignore updateChooserCountsAndModelAfterUserSelection()585 public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException { 586 Intent sendIntent = createSendTextIntent(); 587 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 588 589 setupResolverControllers(resolvedComponentInfos); 590 591 final IChooserWrapper activity = (IChooserWrapper) 592 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 593 waitForIdle(); 594 UsageStatsManager usm = activity.getUsageStatsManager(); 595 verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1)) 596 .topK(any(List.class), anyInt()); 597 assertThat(activity.getIsSelected(), is(false)); 598 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 599 return true; 600 }; 601 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0); 602 DisplayResolveInfo testDri = 603 activity.createTestDisplayResolveInfo( 604 sendIntent, toChoose, "testLabel", "testInfo", sendIntent); 605 onView(withText(toChoose.activityInfo.name)) 606 .perform(click()); 607 waitForIdle(); 608 verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1)) 609 .updateChooserCounts(Mockito.anyString(), any(UserHandle.class), 610 Mockito.anyString()); 611 verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1)) 612 .updateModel(testDri); 613 assertThat(activity.getIsSelected(), is(true)); 614 } 615 616 @Ignore // b/148158199 617 @Test noResultsFromPackageManager()618 public void noResultsFromPackageManager() { 619 setupResolverControllers(null); 620 Intent sendIntent = createSendTextIntent(); 621 final ChooserActivity activity = 622 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 623 final IChooserWrapper wrapper = (IChooserWrapper) activity; 624 625 waitForIdle(); 626 assertThat(activity.isFinishing(), is(false)); 627 628 onView(withId(android.R.id.empty)).check(matches(isDisplayed())); 629 onView(withId(com.android.internal.R.id.profile_pager)).check(matches(not(isDisplayed()))); 630 InstrumentationRegistry.getInstrumentation().runOnMainSync( 631 () -> wrapper.getAdapter().handlePackagesChanged() 632 ); 633 // backward compatibility. looks like we finish when data is empty after package change 634 assertThat(activity.isFinishing(), is(true)); 635 } 636 637 @Test autoLaunchSingleResult()638 public void autoLaunchSingleResult() throws InterruptedException { 639 ResolveInfo[] chosen = new ResolveInfo[1]; 640 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 641 chosen[0] = targetInfo.getResolveInfo(); 642 return true; 643 }; 644 645 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1); 646 setupResolverControllers(resolvedComponentInfos); 647 648 Intent sendIntent = createSendTextIntent(); 649 final ChooserActivity activity = 650 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 651 waitForIdle(); 652 653 assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 654 assertThat(activity.isFinishing(), is(true)); 655 } 656 657 @Test @Ignore hasOtherProfileOneOption()658 public void hasOtherProfileOneOption() { 659 List<ResolvedComponentInfo> personalResolvedComponentInfos = 660 createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10); 661 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); 662 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 663 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 664 665 ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0); 666 Intent sendIntent = createSendTextIntent(); 667 final IChooserWrapper activity = (IChooserWrapper) 668 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 669 waitForIdle(); 670 671 // The other entry is filtered to the other profile slot 672 assertThat(activity.getAdapter().getCount(), is(1)); 673 674 ResolveInfo[] chosen = new ResolveInfo[1]; 675 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 676 chosen[0] = targetInfo.getResolveInfo(); 677 return true; 678 }; 679 680 // Make a stable copy of the components as the original list may be modified 681 List<ResolvedComponentInfo> stableCopy = 682 createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10); 683 waitForIdle(); 684 685 onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))) 686 .perform(click()); 687 waitForIdle(); 688 assertThat(chosen[0], is(toChoose)); 689 } 690 691 @Test @Ignore hasOtherProfileTwoOptionsAndUserSelectsOne()692 public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception { 693 Intent sendIntent = createSendTextIntent(); 694 List<ResolvedComponentInfo> resolvedComponentInfos = 695 createResolvedComponentsForTestWithOtherProfile(3); 696 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 697 698 setupResolverControllers(resolvedComponentInfos); 699 when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen()) 700 .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0)); 701 702 final IChooserWrapper activity = (IChooserWrapper) 703 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 704 waitForIdle(); 705 706 // The other entry is filtered to the other profile slot 707 assertThat(activity.getAdapter().getCount(), is(2)); 708 709 ResolveInfo[] chosen = new ResolveInfo[1]; 710 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 711 chosen[0] = targetInfo.getResolveInfo(); 712 return true; 713 }; 714 715 // Make a stable copy of the components as the original list may be modified 716 List<ResolvedComponentInfo> stableCopy = 717 createResolvedComponentsForTestWithOtherProfile(3); 718 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 719 .perform(click()); 720 waitForIdle(); 721 assertThat(chosen[0], is(toChoose)); 722 } 723 724 @Test @Ignore hasLastChosenActivityAndOtherProfile()725 public void hasLastChosenActivityAndOtherProfile() throws Exception { 726 Intent sendIntent = createSendTextIntent(); 727 List<ResolvedComponentInfo> resolvedComponentInfos = 728 createResolvedComponentsForTestWithOtherProfile(3); 729 ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0); 730 731 setupResolverControllers(resolvedComponentInfos); 732 733 final IChooserWrapper activity = (IChooserWrapper) 734 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 735 waitForIdle(); 736 737 // The other entry is filtered to the last used slot 738 assertThat(activity.getAdapter().getCount(), is(2)); 739 740 ResolveInfo[] chosen = new ResolveInfo[1]; 741 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 742 chosen[0] = targetInfo.getResolveInfo(); 743 return true; 744 }; 745 746 // Make a stable copy of the components as the original list may be modified 747 List<ResolvedComponentInfo> stableCopy = 748 createResolvedComponentsForTestWithOtherProfile(3); 749 onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)) 750 .perform(click()); 751 waitForIdle(); 752 assertThat(chosen[0], is(toChoose)); 753 } 754 755 @Test 756 @Ignore("b/285309527") testFilePlusTextSharing_ExcludeText()757 public void testFilePlusTextSharing_ExcludeText() { 758 Uri uri = createTestContentProviderUri(null, "image/png"); 759 Intent sendIntent = createSendImageIntent(uri); 760 mFakeImageLoader.setBitmap(uri, createBitmap()); 761 sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google"); 762 763 List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList( 764 ResolverDataProvider.createResolvedComponentInfo( 765 new ComponentName("org.imageviewer", "ImageTarget"), 766 sendIntent, PERSONAL_USER_HANDLE), 767 ResolverDataProvider.createResolvedComponentInfo( 768 new ComponentName("org.textviewer", "UriTarget"), 769 new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE) 770 ); 771 772 setupResolverControllers(resolvedComponentInfos); 773 774 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 775 waitForIdle(); 776 777 onView(withId(R.id.include_text_action)) 778 .check(matches(isDisplayed())) 779 .perform(click()); 780 waitForIdle(); 781 782 onView(withId(R.id.content_preview_text)).check(matches(withText("File only"))); 783 784 AtomicReference<Intent> launchedIntentRef = new AtomicReference<>(); 785 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 786 launchedIntentRef.set(targetInfo.getTargetIntent()); 787 return true; 788 }; 789 790 onView(withText(resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.name)) 791 .perform(click()); 792 waitForIdle(); 793 assertThat(launchedIntentRef.get().hasExtra(Intent.EXTRA_TEXT)).isFalse(); 794 } 795 796 @Test 797 @Ignore("b/285309527") testFilePlusTextSharing_RemoveAndAddBackText()798 public void testFilePlusTextSharing_RemoveAndAddBackText() { 799 Uri uri = createTestContentProviderUri("application/pdf", "image/png"); 800 Intent sendIntent = createSendImageIntent(uri); 801 mFakeImageLoader.setBitmap(uri, createBitmap()); 802 final String text = "https://google.com/search?q=google"; 803 sendIntent.putExtra(Intent.EXTRA_TEXT, text); 804 805 List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList( 806 ResolverDataProvider.createResolvedComponentInfo( 807 new ComponentName("org.imageviewer", "ImageTarget"), 808 sendIntent, PERSONAL_USER_HANDLE), 809 ResolverDataProvider.createResolvedComponentInfo( 810 new ComponentName("org.textviewer", "UriTarget"), 811 new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE) 812 ); 813 814 setupResolverControllers(resolvedComponentInfos); 815 816 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 817 waitForIdle(); 818 819 onView(withId(R.id.include_text_action)) 820 .check(matches(isDisplayed())) 821 .perform(click()); 822 waitForIdle(); 823 onView(withId(R.id.content_preview_text)).check(matches(withText("File only"))); 824 825 onView(withId(R.id.include_text_action)) 826 .perform(click()); 827 waitForIdle(); 828 829 onView(withId(R.id.content_preview_text)).check(matches(withText(text))); 830 831 AtomicReference<Intent> launchedIntentRef = new AtomicReference<>(); 832 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 833 launchedIntentRef.set(targetInfo.getTargetIntent()); 834 return true; 835 }; 836 837 onView(withText(resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.name)) 838 .perform(click()); 839 waitForIdle(); 840 assertThat(launchedIntentRef.get().getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(text); 841 } 842 843 @Test 844 @Ignore("b/285309527") testFilePlusTextSharing_TextExclusionDoesNotAffectAlternativeIntent()845 public void testFilePlusTextSharing_TextExclusionDoesNotAffectAlternativeIntent() { 846 Uri uri = createTestContentProviderUri("image/png", null); 847 Intent sendIntent = createSendImageIntent(uri); 848 mFakeImageLoader.setBitmap(uri, createBitmap()); 849 sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google"); 850 851 Intent alternativeIntent = createSendTextIntent(); 852 final String text = "alternative intent"; 853 alternativeIntent.putExtra(Intent.EXTRA_TEXT, text); 854 855 List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList( 856 ResolverDataProvider.createResolvedComponentInfo( 857 new ComponentName("org.imageviewer", "ImageTarget"), 858 sendIntent, PERSONAL_USER_HANDLE), 859 ResolverDataProvider.createResolvedComponentInfo( 860 new ComponentName("org.textviewer", "UriTarget"), 861 alternativeIntent, PERSONAL_USER_HANDLE) 862 ); 863 864 setupResolverControllers(resolvedComponentInfos); 865 866 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 867 waitForIdle(); 868 869 onView(withId(R.id.include_text_action)) 870 .check(matches(isDisplayed())) 871 .perform(click()); 872 waitForIdle(); 873 874 AtomicReference<Intent> launchedIntentRef = new AtomicReference<>(); 875 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 876 launchedIntentRef.set(targetInfo.getTargetIntent()); 877 return true; 878 }; 879 880 onView(withText(resolvedComponentInfos.get(1).getResolveInfoAt(0).activityInfo.name)) 881 .perform(click()); 882 waitForIdle(); 883 assertThat(launchedIntentRef.get().getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(text); 884 } 885 886 @Test 887 @Ignore("b/285309527") testImagePlusTextSharing_failedThumbnailAndExcludedText_textChanges()888 public void testImagePlusTextSharing_failedThumbnailAndExcludedText_textChanges() { 889 Uri uri = createTestContentProviderUri("image/png", null); 890 Intent sendIntent = createSendImageIntent(uri); 891 sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google"); 892 893 List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList( 894 ResolverDataProvider.createResolvedComponentInfo( 895 new ComponentName("org.imageviewer", "ImageTarget"), 896 sendIntent, PERSONAL_USER_HANDLE), 897 ResolverDataProvider.createResolvedComponentInfo( 898 new ComponentName("org.textviewer", "UriTarget"), 899 new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE) 900 ); 901 902 setupResolverControllers(resolvedComponentInfos); 903 904 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 905 waitForIdle(); 906 907 onView(withId(R.id.include_text_action)) 908 .check(matches(isDisplayed())) 909 .perform(click()); 910 waitForIdle(); 911 912 onView(withId(R.id.image_view)) 913 .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); 914 onView(withId(R.id.content_preview_text)) 915 .check(matches(allOf(isDisplayed(), withText("Image only")))); 916 } 917 918 @Test copyTextToClipboard()919 public void copyTextToClipboard() { 920 Intent sendIntent = createSendTextIntent(); 921 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 922 923 setupResolverControllers(resolvedComponentInfos); 924 925 final ChooserActivity activity = 926 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 927 waitForIdle(); 928 929 onView(withId(R.id.copy)).check(matches(isDisplayed())); 930 onView(withId(R.id.copy)).perform(click()); 931 ClipboardManager clipboard = (ClipboardManager) activity.getSystemService( 932 Context.CLIPBOARD_SERVICE); 933 ClipData clipData = clipboard.getPrimaryClip(); 934 assertThat(clipData).isNotNull(); 935 assertThat(clipData.getItemAt(0).getText()).isEqualTo("testing intent sending"); 936 937 ClipDescription clipDescription = clipData.getDescription(); 938 assertThat("text/plain", is(clipDescription.getMimeType(0))); 939 940 assertEquals(mActivityRule.getActivityResult().getResultCode(), RESULT_OK); 941 } 942 943 @Test copyTextToClipboardLogging()944 public void copyTextToClipboardLogging() { 945 Intent sendIntent = createSendTextIntent(); 946 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 947 948 setupResolverControllers(resolvedComponentInfos); 949 950 ChooserWrapperActivity activity = 951 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 952 waitForIdle(); 953 954 onView(withId(R.id.copy)).check(matches(isDisplayed())); 955 onView(withId(R.id.copy)).perform(click()); 956 FakeEventLog eventLog = getEventLog(activity); 957 assertThat(eventLog.getActionSelected()) 958 .isEqualTo(new FakeEventLog.ActionSelected( 959 /* targetType = */ EventLog.SELECTION_TYPE_COPY)); 960 } 961 962 @Test 963 @Ignore testNearbyShareLogging()964 public void testNearbyShareLogging() throws Exception { 965 Intent sendIntent = createSendTextIntent(); 966 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 967 968 setupResolverControllers(resolvedComponentInfos); 969 970 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 971 waitForIdle(); 972 973 onView(withId(com.android.internal.R.id.chooser_nearby_button)) 974 .check(matches(isDisplayed())); 975 onView(withId(com.android.internal.R.id.chooser_nearby_button)).perform(click()); 976 977 // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events. 978 } 979 980 @Test @Ignore testEditImageLogs()981 public void testEditImageLogs() { 982 983 Uri uri = createTestContentProviderUri("image/png", null); 984 Intent sendIntent = createSendImageIntent(uri); 985 mFakeImageLoader.setBitmap(uri, createBitmap()); 986 987 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 988 989 setupResolverControllers(resolvedComponentInfos); 990 991 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 992 waitForIdle(); 993 994 onView(withId(com.android.internal.R.id.chooser_edit_button)).check(matches(isDisplayed())); 995 onView(withId(com.android.internal.R.id.chooser_edit_button)).perform(click()); 996 997 // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events. 998 } 999 1000 1001 @Test oneVisibleImagePreview()1002 public void oneVisibleImagePreview() { 1003 Uri uri = createTestContentProviderUri("image/png", null); 1004 1005 ArrayList<Uri> uris = new ArrayList<>(); 1006 uris.add(uri); 1007 1008 Intent sendIntent = createSendUriIntentWithPreview(uris); 1009 mFakeImageLoader.setBitmap(uri, createWideBitmap()); 1010 1011 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1012 1013 setupResolverControllers(resolvedComponentInfos); 1014 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1015 waitForIdle(); 1016 onView(withId(R.id.scrollable_image_preview)) 1017 .check((view, exception) -> { 1018 if (exception != null) { 1019 throw exception; 1020 } 1021 RecyclerView recyclerView = (RecyclerView) view; 1022 RecyclerViewExt.endAnimations(recyclerView); 1023 assertThat("recyclerView adapter item count", 1024 recyclerView.getAdapter().getItemCount(), is(1)); 1025 assertThat("recyclerView child view count", 1026 recyclerView.getChildCount(), is(1)); 1027 View imageView = recyclerView.getChildAt(0); 1028 Rect rect = new Rect(); 1029 boolean isPartiallyVisible = imageView.getGlobalVisibleRect(rect); 1030 assertThat( 1031 "image preview view is not fully visible", 1032 isPartiallyVisible 1033 && rect.width() == imageView.getWidth() 1034 && rect.height() == imageView.getHeight()); 1035 }); 1036 } 1037 1038 @Test allThumbnailsFailedToLoad_hidePreview()1039 public void allThumbnailsFailedToLoad_hidePreview() { 1040 Uri uri = createTestContentProviderUri("image/jpg", null); 1041 1042 ArrayList<Uri> uris = new ArrayList<>(); 1043 uris.add(uri); 1044 uris.add(uri); 1045 1046 Intent sendIntent = createSendUriIntentWithPreview(uris); 1047 1048 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1049 1050 setupResolverControllers(resolvedComponentInfos); 1051 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1052 waitForIdle(); 1053 onView(withId(R.id.scrollable_image_preview)) 1054 .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); 1055 } 1056 1057 @Test(timeout = 4_000) testSlowUriMetadata_fallbackToFilePreview()1058 public void testSlowUriMetadata_fallbackToFilePreview() { 1059 Uri uri = createTestContentProviderUri( 1060 "application/pdf", "image/png", /*streamTypeTimeout=*/8_000); 1061 ArrayList<Uri> uris = new ArrayList<>(1); 1062 uris.add(uri); 1063 Intent sendIntent = createSendUriIntentWithPreview(uris); 1064 mFakeImageLoader.setBitmap(uri, createBitmap()); 1065 1066 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1067 1068 setupResolverControllers(resolvedComponentInfos); 1069 // The preview type resolution is expected to timeout and default to file preview, otherwise 1070 // the test should timeout. 1071 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1072 waitForIdle(); 1073 1074 onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); 1075 onView(withId(R.id.content_preview_filename)).check(matches(withText("image.png"))); 1076 onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); 1077 } 1078 1079 @Test(timeout = 4_000) testSendManyFilesWithSmallMetadataDelayAndOneImage_fallbackToFilePreviewUi()1080 public void testSendManyFilesWithSmallMetadataDelayAndOneImage_fallbackToFilePreviewUi() { 1081 Uri fileUri = createTestContentProviderUri( 1082 "application/pdf", "application/pdf", /*streamTypeTimeout=*/300); 1083 Uri imageUri = createTestContentProviderUri("application/pdf", "image/png"); 1084 ArrayList<Uri> uris = new ArrayList<>(50); 1085 for (int i = 0; i < 49; i++) { 1086 uris.add(fileUri); 1087 } 1088 uris.add(imageUri); 1089 Intent sendIntent = createSendUriIntentWithPreview(uris); 1090 mFakeImageLoader.setBitmap(imageUri, createBitmap()); 1091 1092 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1093 setupResolverControllers(resolvedComponentInfos); 1094 // The preview type resolution is expected to timeout and default to file preview, otherwise 1095 // the test should timeout. 1096 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1097 1098 waitForIdle(); 1099 1100 onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); 1101 onView(withId(R.id.content_preview_filename)).check(matches(withText("image.png"))); 1102 onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); 1103 } 1104 1105 @Test testManyVisibleImagePreview_ScrollableImagePreview()1106 public void testManyVisibleImagePreview_ScrollableImagePreview() { 1107 Uri uri = createTestContentProviderUri("image/png", null); 1108 1109 ArrayList<Uri> uris = new ArrayList<>(); 1110 uris.add(uri); 1111 uris.add(uri); 1112 uris.add(uri); 1113 uris.add(uri); 1114 uris.add(uri); 1115 uris.add(uri); 1116 uris.add(uri); 1117 uris.add(uri); 1118 uris.add(uri); 1119 uris.add(uri); 1120 1121 Intent sendIntent = createSendUriIntentWithPreview(uris); 1122 mFakeImageLoader.setBitmap(uri, createBitmap()); 1123 1124 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1125 1126 setupResolverControllers(resolvedComponentInfos); 1127 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1128 waitForIdle(); 1129 onView(withId(R.id.scrollable_image_preview)) 1130 .perform(RecyclerViewActions.scrollToLastPosition()) 1131 .check((view, exception) -> { 1132 if (exception != null) { 1133 throw exception; 1134 } 1135 RecyclerView recyclerView = (RecyclerView) view; 1136 assertThat(recyclerView.getAdapter().getItemCount(), is(uris.size())); 1137 }); 1138 } 1139 1140 @Test(timeout = 4_000) testPartiallyLoadedMetadata_previewIsShownForTheLoadedPart()1141 public void testPartiallyLoadedMetadata_previewIsShownForTheLoadedPart() { 1142 Uri imgOneUri = createTestContentProviderUri("image/png", null); 1143 Uri imgTwoUri = createTestContentProviderUri("image/png", null) 1144 .buildUpon() 1145 .path("image-2.png") 1146 .build(); 1147 Uri docUri = createTestContentProviderUri("application/pdf", "image/png", 8_000); 1148 ArrayList<Uri> uris = new ArrayList<>(2); 1149 // two large previews to fill the screen and be presented right away and one 1150 // document that would be delayed by the URI metadata reading 1151 uris.add(imgOneUri); 1152 uris.add(imgTwoUri); 1153 uris.add(docUri); 1154 1155 Intent sendIntent = createSendUriIntentWithPreview(uris); 1156 mFakeImageLoader.setBitmap(imgOneUri, createWideBitmap(Color.RED)); 1157 mFakeImageLoader.setBitmap(imgTwoUri, createWideBitmap(Color.GREEN)); 1158 mFakeImageLoader.setBitmap(docUri, createWideBitmap(Color.BLUE)); 1159 1160 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1161 setupResolverControllers(resolvedComponentInfos); 1162 1163 // the preview type is expected to be resolved quickly based on the first provided URI 1164 // metadata. If, instead, it is dependent on the third URI metadata, the test should either 1165 // timeout or (more probably due to inner timeout) default to file preview type; anyway the 1166 // test will fail. 1167 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1168 waitForIdle(); 1169 1170 onView(withId(R.id.scrollable_image_preview)) 1171 .check((view, exception) -> { 1172 if (exception != null) { 1173 throw exception; 1174 } 1175 RecyclerView recyclerView = (RecyclerView) view; 1176 RecyclerViewExt.endAnimations(recyclerView); 1177 assertThat(recyclerView.getChildCount()).isAtLeast(1); 1178 // the first view is a preview 1179 View imageView = recyclerView.getChildAt(0).findViewById(R.id.image); 1180 assertThat(imageView).isNotNull(); 1181 }) 1182 .perform(RecyclerViewActions.scrollToLastPosition()) 1183 .check((view, exception) -> { 1184 if (exception != null) { 1185 throw exception; 1186 } 1187 RecyclerView recyclerView = (RecyclerView) view; 1188 assertThat(recyclerView.getChildCount()).isAtLeast(1); 1189 // check that the last view is a loading indicator 1190 View loadingIndicator = 1191 recyclerView.getChildAt(recyclerView.getChildCount() - 1); 1192 assertThat(loadingIndicator).isNotNull(); 1193 }); 1194 waitForIdle(); 1195 } 1196 1197 @Test testImageAndTextPreview()1198 public void testImageAndTextPreview() { 1199 final Uri uri = createTestContentProviderUri("image/png", null); 1200 final String sharedText = "text-" + System.currentTimeMillis(); 1201 1202 ArrayList<Uri> uris = new ArrayList<>(); 1203 uris.add(uri); 1204 1205 Intent sendIntent = createSendUriIntentWithPreview(uris); 1206 sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText); 1207 mFakeImageLoader.setBitmap(uri, createBitmap()); 1208 1209 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1210 1211 setupResolverControllers(resolvedComponentInfos); 1212 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1213 waitForIdle(); 1214 onView(withText(sharedText)) 1215 .check(matches(isDisplayed())); 1216 } 1217 1218 @Test test_shareImageWithRichText_RichTextIsDisplayed()1219 public void test_shareImageWithRichText_RichTextIsDisplayed() { 1220 final Uri uri = createTestContentProviderUri("image/png", null); 1221 final CharSequence sharedText = new SpannableStringBuilder() 1222 .append( 1223 "text-", 1224 new StyleSpan(Typeface.BOLD_ITALIC), 1225 Spannable.SPAN_INCLUSIVE_EXCLUSIVE) 1226 .append( 1227 Long.toString(System.currentTimeMillis()), 1228 new ForegroundColorSpan(Color.RED), 1229 Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 1230 1231 ArrayList<Uri> uris = new ArrayList<>(); 1232 uris.add(uri); 1233 1234 Intent sendIntent = createSendUriIntentWithPreview(uris); 1235 sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText); 1236 mFakeImageLoader.setBitmap(uri, createBitmap()); 1237 1238 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1239 1240 setupResolverControllers(resolvedComponentInfos); 1241 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1242 waitForIdle(); 1243 onView(withText(sharedText.toString())) 1244 .check(matches(isDisplayed())) 1245 .check((view, e) -> { 1246 if (e != null) { 1247 throw e; 1248 } 1249 assertThat(view).isInstanceOf(TextView.class); 1250 CharSequence text = ((TextView) view).getText(); 1251 assertThat(text).isInstanceOf(Spanned.class); 1252 Spanned spanned = (Spanned) text; 1253 Object[] spans = spanned.getSpans(0, text.length(), Object.class); 1254 assertThat(spans).hasLength(2); 1255 assertThat(spanned.getSpans(0, 5, StyleSpan.class)).hasLength(1); 1256 assertThat(spanned.getSpans(5, text.length(), ForegroundColorSpan.class)) 1257 .hasLength(1); 1258 }); 1259 } 1260 1261 @Test testTextPreviewWhenTextIsSharedWithMultipleImages()1262 public void testTextPreviewWhenTextIsSharedWithMultipleImages() { 1263 final Uri uri = createTestContentProviderUri("image/png", null); 1264 final String sharedText = "text-" + System.currentTimeMillis(); 1265 1266 ArrayList<Uri> uris = new ArrayList<>(); 1267 uris.add(uri); 1268 uris.add(uri); 1269 1270 Intent sendIntent = createSendUriIntentWithPreview(uris); 1271 sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText); 1272 mFakeImageLoader.setBitmap(uri, createBitmap()); 1273 1274 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1275 1276 when( 1277 ChooserActivityOverrideData 1278 .getInstance() 1279 .resolverListController 1280 .getResolversForIntentAsUser( 1281 Mockito.anyBoolean(), 1282 Mockito.anyBoolean(), 1283 Mockito.anyBoolean(), 1284 Mockito.isA(List.class), 1285 Mockito.any(UserHandle.class))) 1286 .thenReturn(resolvedComponentInfos); 1287 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1288 waitForIdle(); 1289 onView(withText(sharedText)).check(matches(isDisplayed())); 1290 } 1291 1292 @Test testOnCreateLogging()1293 public void testOnCreateLogging() { 1294 Intent sendIntent = createSendTextIntent(); 1295 sendIntent.setType(TEST_MIME_TYPE); 1296 1297 ChooserWrapperActivity activity = 1298 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test")); 1299 waitForIdle(); 1300 1301 FakeEventLog eventLog = getEventLog(activity); 1302 FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown(); 1303 assertThat(event).isNotNull(); 1304 assertThat(event.isWorkProfile()).isFalse(); 1305 assertThat(event.getTargetMimeType()).isEqualTo(TEST_MIME_TYPE); 1306 } 1307 1308 @Test testOnCreateLoggingFromWorkProfile()1309 public void testOnCreateLoggingFromWorkProfile() { 1310 Intent sendIntent = createSendTextIntent(); 1311 sendIntent.setType(TEST_MIME_TYPE); 1312 1313 // Launch as work user. 1314 mFakeUserRepo.addUser(WORK_USER, true); 1315 mApplicationUser = WORK_PROFILE_USER_HANDLE; 1316 1317 ChooserWrapperActivity activity = 1318 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test")); 1319 waitForIdle(); 1320 1321 FakeEventLog eventLog = getEventLog(activity); 1322 FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown(); 1323 assertThat(event).isNotNull(); 1324 assertThat(event.isWorkProfile()).isTrue(); 1325 assertThat(event.getTargetMimeType()).isEqualTo(TEST_MIME_TYPE); 1326 } 1327 1328 @Test testEmptyPreviewLogging()1329 public void testEmptyPreviewLogging() { 1330 Intent sendIntent = createSendTextIntentWithPreview(null, null); 1331 1332 ChooserWrapperActivity activity = 1333 mActivityRule.launchActivity(Intent.createChooser(sendIntent, 1334 "empty preview logger test")); 1335 waitForIdle(); 1336 1337 FakeEventLog eventLog = getEventLog(activity); 1338 FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown(); 1339 assertThat(event).isNotNull(); 1340 assertThat(event.isWorkProfile()).isFalse(); 1341 assertThat(event.getTargetMimeType()).isNull(); 1342 } 1343 1344 @Test testTitlePreviewLogging()1345 public void testTitlePreviewLogging() { 1346 Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null); 1347 1348 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1349 1350 setupResolverControllers(resolvedComponentInfos); 1351 1352 ChooserWrapperActivity activity = 1353 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1354 waitForIdle(); 1355 1356 FakeEventLog eventLog = getEventLog(activity); 1357 assertThat(eventLog.getActionShareWithPreview()) 1358 .isEqualTo(new FakeEventLog.ActionShareWithPreview( 1359 /* previewType = */ CONTENT_PREVIEW_TEXT)); 1360 } 1361 1362 @Test testImagePreviewLogging()1363 public void testImagePreviewLogging() { 1364 Uri uri = createTestContentProviderUri("image/png", null); 1365 1366 ArrayList<Uri> uris = new ArrayList<>(); 1367 uris.add(uri); 1368 1369 Intent sendIntent = createSendUriIntentWithPreview(uris); 1370 mFakeImageLoader.setBitmap(uri, createBitmap()); 1371 1372 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1373 1374 setupResolverControllers(resolvedComponentInfos); 1375 1376 ChooserWrapperActivity activity = 1377 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1378 waitForIdle(); 1379 1380 FakeEventLog eventLog = getEventLog(activity); 1381 assertThat(eventLog.getActionShareWithPreview()) 1382 .isEqualTo(new FakeEventLog.ActionShareWithPreview( 1383 /* previewType = */ CONTENT_PREVIEW_IMAGE)); 1384 } 1385 1386 @Test oneVisibleFilePreview()1387 public void oneVisibleFilePreview() throws InterruptedException { 1388 Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); 1389 1390 ArrayList<Uri> uris = new ArrayList<>(); 1391 uris.add(uri); 1392 1393 Intent sendIntent = createSendUriIntentWithPreview(uris); 1394 1395 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1396 1397 setupResolverControllers(resolvedComponentInfos); 1398 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1399 waitForIdle(); 1400 onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); 1401 onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); 1402 onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); 1403 } 1404 1405 1406 @Test moreThanOneVisibleFilePreview()1407 public void moreThanOneVisibleFilePreview() throws InterruptedException { 1408 Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); 1409 1410 ArrayList<Uri> uris = new ArrayList<>(); 1411 uris.add(uri); 1412 uris.add(uri); 1413 uris.add(uri); 1414 1415 Intent sendIntent = createSendUriIntentWithPreview(uris); 1416 1417 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1418 1419 setupResolverControllers(resolvedComponentInfos); 1420 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1421 waitForIdle(); 1422 onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); 1423 onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); 1424 onView(withId(R.id.content_preview_more_files)).check(matches(isDisplayed())); 1425 onView(withId(R.id.content_preview_more_files)).check(matches(withText("+ 2 more files"))); 1426 onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); 1427 } 1428 1429 @Test contentProviderThrowSecurityException()1430 public void contentProviderThrowSecurityException() throws InterruptedException { 1431 Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); 1432 1433 ArrayList<Uri> uris = new ArrayList<>(); 1434 uris.add(uri); 1435 1436 Intent sendIntent = createSendUriIntentWithPreview(uris); 1437 1438 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1439 setupResolverControllers(resolvedComponentInfos); 1440 1441 ChooserActivityOverrideData.getInstance().resolverForceException = true; 1442 1443 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1444 waitForIdle(); 1445 onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); 1446 onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); 1447 onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); 1448 } 1449 1450 @Test contentProviderReturnsNoColumns()1451 public void contentProviderReturnsNoColumns() throws InterruptedException { 1452 Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); 1453 1454 ArrayList<Uri> uris = new ArrayList<>(); 1455 uris.add(uri); 1456 uris.add(uri); 1457 1458 Intent sendIntent = createSendUriIntentWithPreview(uris); 1459 1460 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1461 setupResolverControllers(resolvedComponentInfos); 1462 1463 Cursor cursor = mock(Cursor.class); 1464 when(cursor.getCount()).thenReturn(1); 1465 Mockito.doNothing().when(cursor).close(); 1466 when(cursor.moveToFirst()).thenReturn(true); 1467 when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1); 1468 1469 ChooserActivityOverrideData.getInstance().resolverCursor = cursor; 1470 1471 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1472 waitForIdle(); 1473 onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); 1474 onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); 1475 onView(withId(R.id.content_preview_more_files)).check(matches(isDisplayed())); 1476 onView(withId(R.id.content_preview_more_files)).check(matches(withText("+ 1 more file"))); 1477 onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); 1478 } 1479 1480 @Test testGetBaseScore()1481 public void testGetBaseScore() { 1482 final float testBaseScore = 0.89f; 1483 1484 Intent sendIntent = createSendTextIntent(); 1485 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1486 1487 setupResolverControllers(resolvedComponentInfos); 1488 1489 when( 1490 ChooserActivityOverrideData 1491 .getInstance() 1492 .resolverListController 1493 .getScore(Mockito.isA(DisplayResolveInfo.class))) 1494 .thenReturn(testBaseScore); 1495 1496 final IChooserWrapper activity = (IChooserWrapper) 1497 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1498 waitForIdle(); 1499 1500 final DisplayResolveInfo testDri = 1501 activity.createTestDisplayResolveInfo( 1502 sendIntent, 1503 ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE), 1504 "testLabel", 1505 "testInfo", 1506 sendIntent); 1507 final ChooserListAdapter adapter = activity.getAdapter(); 1508 1509 assertThat(adapter.getBaseScore(null, 0), is(CALLER_TARGET_SCORE_BOOST)); 1510 assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore)); 1511 assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_CHOOSER_TARGET), is(testBaseScore)); 1512 assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE), 1513 is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST)); 1514 assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER), 1515 is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST)); 1516 } 1517 1518 // This test is too long and too slow and should not be taken as an example for future tests. 1519 @Test testDirectTargetSelectionLogging()1520 public void testDirectTargetSelectionLogging() { 1521 Intent sendIntent = createSendTextIntent(); 1522 // We need app targets for direct targets to get displayed 1523 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1524 setupResolverControllers(resolvedComponentInfos); 1525 1526 // create test shortcut loader factory, remember loaders and their callbacks 1527 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 1528 createShortcutLoaderFactory(); 1529 1530 // Start activity 1531 ChooserWrapperActivity activity = 1532 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1533 waitForIdle(); 1534 1535 // verify that ShortcutLoader was queried 1536 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 1537 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 1538 verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); 1539 1540 // send shortcuts 1541 assertThat( 1542 "Wrong number of app targets", 1543 appTargets.getValue().length, 1544 is(resolvedComponentInfos.size())); 1545 List<ChooserTarget> serviceTargets = createDirectShareTargets(1, ""); 1546 ShortcutLoader.Result result = new ShortcutLoader.Result( 1547 true, 1548 appTargets.getValue(), 1549 new ShortcutLoader.ShortcutResultInfo[] { 1550 new ShortcutLoader.ShortcutResultInfo( 1551 appTargets.getValue()[0], 1552 serviceTargets 1553 ) 1554 }, 1555 new HashMap<>(), 1556 new HashMap<>() 1557 ); 1558 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 1559 waitForIdle(); 1560 1561 final ChooserListAdapter activeAdapter = activity.getAdapter(); 1562 assertThat( 1563 "Chooser should have 3 targets (2 apps, 1 direct)", 1564 activeAdapter.getCount(), 1565 is(3)); 1566 assertThat( 1567 "Chooser should have exactly one selectable direct target", 1568 activeAdapter.getSelectableServiceTargetCount(), 1569 is(1)); 1570 assertThat( 1571 "The resolver info must match the resolver info used to create the target", 1572 activeAdapter.getItem(0).getResolveInfo(), 1573 is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 1574 1575 // Click on the direct target 1576 String name = serviceTargets.get(0).getTitle().toString(); 1577 onView(withText(name)) 1578 .perform(click()); 1579 waitForIdle(); 1580 1581 FakeEventLog eventLog = getEventLog(activity); 1582 assertThat(eventLog.getShareTargetSelected()).hasSize(1); 1583 FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0); 1584 assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE); 1585 assertThat(call.getDirectTargetAlsoRanked()).isEqualTo(-1); 1586 var hashResult = call.getDirectTargetHashed(); 1587 var hash = hashResult == null ? "" : hashResult.hashedString; 1588 assertWithMessage("Hash is not predictable but must be obfuscated") 1589 .that(hash).isNotEqualTo(name); 1590 } 1591 1592 // This test is too long and too slow and should not be taken as an example for future tests. 1593 @Test testDirectTargetLoggingWithRankedAppTarget()1594 public void testDirectTargetLoggingWithRankedAppTarget() { 1595 Intent sendIntent = createSendTextIntent(); 1596 // We need app targets for direct targets to get displayed 1597 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1598 setupResolverControllers(resolvedComponentInfos); 1599 1600 // create test shortcut loader factory, remember loaders and their callbacks 1601 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 1602 createShortcutLoaderFactory(); 1603 1604 // Start activity 1605 ChooserWrapperActivity activity = 1606 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1607 waitForIdle(); 1608 1609 // verify that ShortcutLoader was queried 1610 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 1611 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 1612 verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); 1613 1614 // send shortcuts 1615 assertThat( 1616 "Wrong number of app targets", 1617 appTargets.getValue().length, 1618 is(resolvedComponentInfos.size())); 1619 List<ChooserTarget> serviceTargets = createDirectShareTargets( 1620 1, 1621 resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName); 1622 ShortcutLoader.Result result = new ShortcutLoader.Result( 1623 true, 1624 appTargets.getValue(), 1625 new ShortcutLoader.ShortcutResultInfo[] { 1626 new ShortcutLoader.ShortcutResultInfo( 1627 appTargets.getValue()[0], 1628 serviceTargets 1629 ) 1630 }, 1631 new HashMap<>(), 1632 new HashMap<>() 1633 ); 1634 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 1635 waitForIdle(); 1636 1637 final ChooserListAdapter activeAdapter = activity.getAdapter(); 1638 assertThat( 1639 "Chooser should have 3 targets (2 apps, 1 direct)", 1640 activeAdapter.getCount(), 1641 is(3)); 1642 assertThat( 1643 "Chooser should have exactly one selectable direct target", 1644 activeAdapter.getSelectableServiceTargetCount(), 1645 is(1)); 1646 assertThat( 1647 "The resolver info must match the resolver info used to create the target", 1648 activeAdapter.getItem(0).getResolveInfo(), 1649 is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 1650 1651 // Click on the direct target 1652 String name = serviceTargets.get(0).getTitle().toString(); 1653 onView(withText(name)) 1654 .perform(click()); 1655 waitForIdle(); 1656 1657 FakeEventLog eventLog = getEventLog(activity); 1658 assertThat(eventLog.getShareTargetSelected()).hasSize(1); 1659 FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0); 1660 1661 assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE); 1662 assertThat(call.getDirectTargetAlsoRanked()).isEqualTo(0); 1663 } 1664 1665 @Test testShortcutTargetWithApplyAppLimits()1666 public void testShortcutTargetWithApplyAppLimits() { 1667 // Set up resources 1668 Resources resources = Mockito.spy( 1669 InstrumentationRegistry.getInstrumentation().getContext().getResources()); 1670 ChooserActivityOverrideData.getInstance().resources = resources; 1671 doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp); 1672 Intent sendIntent = createSendTextIntent(); 1673 // We need app targets for direct targets to get displayed 1674 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1675 setupResolverControllers(resolvedComponentInfos); 1676 1677 // create test shortcut loader factory, remember loaders and their callbacks 1678 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 1679 createShortcutLoaderFactory(); 1680 1681 // Start activity 1682 final IChooserWrapper activity = (IChooserWrapper) mActivityRule 1683 .launchActivity(Intent.createChooser(sendIntent, null)); 1684 waitForIdle(); 1685 1686 // verify that ShortcutLoader was queried 1687 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 1688 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 1689 verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); 1690 1691 // send shortcuts 1692 assertThat( 1693 "Wrong number of app targets", 1694 appTargets.getValue().length, 1695 is(resolvedComponentInfos.size())); 1696 List<ChooserTarget> serviceTargets = createDirectShareTargets( 1697 2, 1698 resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName); 1699 ShortcutLoader.Result result = new ShortcutLoader.Result( 1700 true, 1701 appTargets.getValue(), 1702 new ShortcutLoader.ShortcutResultInfo[] { 1703 new ShortcutLoader.ShortcutResultInfo( 1704 appTargets.getValue()[0], 1705 serviceTargets 1706 ) 1707 }, 1708 new HashMap<>(), 1709 new HashMap<>() 1710 ); 1711 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 1712 waitForIdle(); 1713 1714 final ChooserListAdapter activeAdapter = activity.getAdapter(); 1715 assertThat( 1716 "Chooser should have 3 targets (2 apps, 1 direct)", 1717 activeAdapter.getCount(), 1718 is(3)); 1719 assertThat( 1720 "Chooser should have exactly one selectable direct target", 1721 activeAdapter.getSelectableServiceTargetCount(), 1722 is(1)); 1723 assertThat( 1724 "The resolver info must match the resolver info used to create the target", 1725 activeAdapter.getItem(0).getResolveInfo(), 1726 is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 1727 assertThat( 1728 "The display label must match", 1729 activeAdapter.getItem(0).getDisplayLabel(), 1730 is("testTitle0")); 1731 } 1732 1733 @Test testShortcutTargetWithoutApplyAppLimits()1734 public void testShortcutTargetWithoutApplyAppLimits() { 1735 setDeviceConfigProperty( 1736 SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI, 1737 Boolean.toString(false)); 1738 // Set up resources 1739 Resources resources = Mockito.spy( 1740 InstrumentationRegistry.getInstrumentation().getContext().getResources()); 1741 ChooserActivityOverrideData.getInstance().resources = resources; 1742 doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp); 1743 Intent sendIntent = createSendTextIntent(); 1744 // We need app targets for direct targets to get displayed 1745 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1746 setupResolverControllers(resolvedComponentInfos); 1747 1748 // create test shortcut loader factory, remember loaders and their callbacks 1749 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 1750 createShortcutLoaderFactory(); 1751 1752 // Start activity 1753 final IChooserWrapper activity = (IChooserWrapper) 1754 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1755 waitForIdle(); 1756 1757 // verify that ShortcutLoader was queried 1758 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 1759 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 1760 verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); 1761 1762 // send shortcuts 1763 assertThat( 1764 "Wrong number of app targets", 1765 appTargets.getValue().length, 1766 is(resolvedComponentInfos.size())); 1767 List<ChooserTarget> serviceTargets = createDirectShareTargets( 1768 2, 1769 resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName); 1770 ShortcutLoader.Result result = new ShortcutLoader.Result( 1771 true, 1772 appTargets.getValue(), 1773 new ShortcutLoader.ShortcutResultInfo[] { 1774 new ShortcutLoader.ShortcutResultInfo( 1775 appTargets.getValue()[0], 1776 serviceTargets 1777 ) 1778 }, 1779 new HashMap<>(), 1780 new HashMap<>() 1781 ); 1782 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 1783 waitForIdle(); 1784 1785 final ChooserListAdapter activeAdapter = activity.getAdapter(); 1786 assertThat( 1787 "Chooser should have 4 targets (2 apps, 2 direct)", 1788 activeAdapter.getCount(), 1789 is(4)); 1790 assertThat( 1791 "Chooser should have exactly two selectable direct target", 1792 activeAdapter.getSelectableServiceTargetCount(), 1793 is(2)); 1794 assertThat( 1795 "The resolver info must match the resolver info used to create the target", 1796 activeAdapter.getItem(0).getResolveInfo(), 1797 is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 1798 assertThat( 1799 "The display label must match", 1800 activeAdapter.getItem(0).getDisplayLabel(), 1801 is("testTitle0")); 1802 assertThat( 1803 "The display label must match", 1804 activeAdapter.getItem(1).getDisplayLabel(), 1805 is("testTitle1")); 1806 } 1807 1808 @Test testLaunchWithCallerProvidedTarget()1809 public void testLaunchWithCallerProvidedTarget() { 1810 setDeviceConfigProperty( 1811 SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI, 1812 Boolean.toString(false)); 1813 // Set up resources 1814 Resources resources = Mockito.spy( 1815 InstrumentationRegistry.getInstrumentation().getContext().getResources()); 1816 ChooserActivityOverrideData.getInstance().resources = resources; 1817 doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp); 1818 1819 // We need app targets for direct targets to get displayed 1820 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1821 setupResolverControllers(resolvedComponentInfos, resolvedComponentInfos); 1822 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 1823 1824 // set caller-provided target 1825 Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null); 1826 String callerTargetLabel = "Caller Target"; 1827 ChooserTarget[] targets = new ChooserTarget[] { 1828 new ChooserTarget( 1829 callerTargetLabel, 1830 Icon.createWithBitmap(createBitmap()), 1831 0.1f, 1832 resolvedComponentInfos.get(0).name, 1833 new Bundle()) 1834 }; 1835 chooserIntent.putExtra(Intent.EXTRA_CHOOSER_TARGETS, targets); 1836 1837 // create test shortcut loader factory, remember loaders and their callbacks 1838 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 1839 createShortcutLoaderFactory(); 1840 1841 // Start activity 1842 final IChooserWrapper activity = (IChooserWrapper) 1843 mActivityRule.launchActivity(chooserIntent); 1844 waitForIdle(); 1845 1846 // verify that ShortcutLoader was queried 1847 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 1848 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 1849 verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture()); 1850 1851 // send shortcuts 1852 assertThat( 1853 "Wrong number of app targets", 1854 appTargets.getValue().length, 1855 is(resolvedComponentInfos.size())); 1856 ShortcutLoader.Result result = new ShortcutLoader.Result( 1857 true, 1858 appTargets.getValue(), 1859 new ShortcutLoader.ShortcutResultInfo[0], 1860 new HashMap<>(), 1861 new HashMap<>()); 1862 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 1863 waitForIdle(); 1864 1865 final ChooserListAdapter activeAdapter = activity.getAdapter(); 1866 assertThat( 1867 "Chooser should have 3 targets (2 apps, 1 direct)", 1868 activeAdapter.getCount(), 1869 is(3)); 1870 assertThat( 1871 "Chooser should have exactly two selectable direct target", 1872 activeAdapter.getSelectableServiceTargetCount(), 1873 is(1)); 1874 assertThat( 1875 "The display label must match", 1876 activeAdapter.getItem(0).getDisplayLabel(), 1877 is(callerTargetLabel)); 1878 1879 // Switch to work profile and ensure that the target *doesn't* show up there. 1880 onView(withText(R.string.resolver_work_tab)).perform(click()); 1881 waitForIdle(); 1882 1883 for (int i = 0; i < activity.getWorkListAdapter().getCount(); i++) { 1884 assertThat( 1885 "Chooser target should not show up in opposite profile", 1886 activity.getWorkListAdapter().getItem(i).getDisplayLabel(), 1887 not(callerTargetLabel)); 1888 } 1889 } 1890 1891 @Test testLaunchWithCustomAction()1892 public void testLaunchWithCustomAction() throws InterruptedException { 1893 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1894 setupResolverControllers(resolvedComponentInfos); 1895 1896 Context testContext = InstrumentationRegistry.getInstrumentation().getContext(); 1897 final String customActionLabel = "Custom Action"; 1898 final String testAction = "test-broadcast-receiver-action"; 1899 Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null); 1900 chooserIntent.putExtra( 1901 Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, 1902 new ChooserAction[] { 1903 new ChooserAction.Builder( 1904 Icon.createWithResource("", Resources.ID_NULL), 1905 customActionLabel, 1906 PendingIntent.getBroadcast( 1907 testContext, 1908 123, 1909 new Intent(testAction), 1910 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT)) 1911 .build() 1912 }); 1913 // Start activity 1914 mActivityRule.launchActivity(chooserIntent); 1915 waitForIdle(); 1916 1917 final CountDownLatch broadcastInvoked = new CountDownLatch(1); 1918 BroadcastReceiver testReceiver = new BroadcastReceiver() { 1919 @Override 1920 public void onReceive(Context context, Intent intent) { 1921 broadcastInvoked.countDown(); 1922 } 1923 }; 1924 testContext.registerReceiver(testReceiver, new IntentFilter(testAction), 1925 Context.RECEIVER_EXPORTED); 1926 1927 try { 1928 onView(withText(customActionLabel)).perform(click()); 1929 assertTrue("Timeout waiting for broadcast", 1930 broadcastInvoked.await(5000, TimeUnit.MILLISECONDS)); 1931 } finally { 1932 testContext.unregisterReceiver(testReceiver); 1933 } 1934 } 1935 1936 @Test testLaunchWithShareModification()1937 public void testLaunchWithShareModification() throws InterruptedException { 1938 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 1939 setupResolverControllers(resolvedComponentInfos); 1940 1941 Context testContext = InstrumentationRegistry.getInstrumentation().getContext(); 1942 final String modifyShareAction = "test-broadcast-receiver-action"; 1943 Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null); 1944 String label = "modify share"; 1945 PendingIntent pendingIntent = PendingIntent.getBroadcast( 1946 testContext, 1947 123, 1948 new Intent(modifyShareAction), 1949 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT); 1950 ChooserAction action = new ChooserAction.Builder(Icon.createWithBitmap( 1951 createBitmap()), label, pendingIntent).build(); 1952 chooserIntent.putExtra( 1953 Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION, 1954 action); 1955 // Start activity 1956 mActivityRule.launchActivity(chooserIntent); 1957 waitForIdle(); 1958 1959 final CountDownLatch broadcastInvoked = new CountDownLatch(1); 1960 BroadcastReceiver testReceiver = new BroadcastReceiver() { 1961 @Override 1962 public void onReceive(Context context, Intent intent) { 1963 broadcastInvoked.countDown(); 1964 } 1965 }; 1966 testContext.registerReceiver(testReceiver, new IntentFilter(modifyShareAction), 1967 Context.RECEIVER_EXPORTED); 1968 1969 try { 1970 onView(withText(label)).perform(click()); 1971 assertTrue("Timeout waiting for broadcast", 1972 broadcastInvoked.await(5000, TimeUnit.MILLISECONDS)); 1973 1974 } finally { 1975 testContext.unregisterReceiver(testReceiver); 1976 } 1977 } 1978 1979 @Test testUpdateMaxTargetsPerRow_columnCountIsUpdated()1980 public void testUpdateMaxTargetsPerRow_columnCountIsUpdated() throws InterruptedException { 1981 updateMaxTargetsPerRowResource(/* targetsPerRow= */ 4); 1982 givenAppTargets(/* appCount= */ 16); 1983 Intent sendIntent = createSendTextIntent(); 1984 final ChooserActivity activity = 1985 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 1986 1987 updateMaxTargetsPerRowResource(/* targetsPerRow= */ 6); 1988 InstrumentationRegistry.getInstrumentation() 1989 .runOnMainSync(() -> activity.onConfigurationChanged( 1990 InstrumentationRegistry.getInstrumentation() 1991 .getContext().getResources().getConfiguration())); 1992 1993 waitForIdle(); 1994 onView(withId(com.android.internal.R.id.resolver_list)) 1995 .check(matches(withGridColumnCount(6))); 1996 } 1997 1998 // This test is too long and too slow and should not be taken as an example for future tests. 1999 @Test @Ignore testDirectTargetLoggingWithAppTargetNotRankedPortrait()2000 public void testDirectTargetLoggingWithAppTargetNotRankedPortrait() 2001 throws InterruptedException { 2002 testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_PORTRAIT, 4); 2003 } 2004 2005 @Test @Ignore testDirectTargetLoggingWithAppTargetNotRankedLandscape()2006 public void testDirectTargetLoggingWithAppTargetNotRankedLandscape() 2007 throws InterruptedException { 2008 testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_LANDSCAPE, 8); 2009 } 2010 testDirectTargetLoggingWithAppTargetNotRanked( int orientation, int appTargetsExpected)2011 private void testDirectTargetLoggingWithAppTargetNotRanked( 2012 int orientation, int appTargetsExpected) { 2013 Configuration configuration = 2014 new Configuration(InstrumentationRegistry.getInstrumentation().getContext() 2015 .getResources().getConfiguration()); 2016 configuration.orientation = orientation; 2017 2018 Resources resources = Mockito.spy( 2019 InstrumentationRegistry.getInstrumentation().getContext().getResources()); 2020 ChooserActivityOverrideData.getInstance().resources = resources; 2021 doReturn(configuration).when(resources).getConfiguration(); 2022 2023 Intent sendIntent = createSendTextIntent(); 2024 // We need app targets for direct targets to get displayed 2025 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15); 2026 setupResolverControllers(resolvedComponentInfos); 2027 2028 // Create direct share target 2029 List<ChooserTarget> serviceTargets = createDirectShareTargets(1, 2030 resolvedComponentInfos.get(14).getResolveInfoAt(0).activityInfo.packageName); 2031 ResolveInfo ri = ResolverDataProvider.createResolveInfo(16, 0, PERSONAL_USER_HANDLE); 2032 2033 // Start activity 2034 ChooserWrapperActivity activity = 2035 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 2036 // Insert the direct share target 2037 Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>(); 2038 directShareToShortcutInfos.put(serviceTargets.get(0), null); 2039 InstrumentationRegistry.getInstrumentation().runOnMainSync( 2040 () -> activity.getAdapter().addServiceResults( 2041 activity.createTestDisplayResolveInfo(sendIntent, 2042 ri, 2043 "testLabel", 2044 "testInfo", 2045 sendIntent), 2046 serviceTargets, 2047 TARGET_TYPE_CHOOSER_TARGET, 2048 directShareToShortcutInfos, 2049 /* directShareToAppTargets */ null) 2050 ); 2051 2052 assertThat( 2053 String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)", 2054 appTargetsExpected + 16, appTargetsExpected), 2055 activity.getAdapter().getCount(), is(appTargetsExpected + 16)); 2056 assertThat("Chooser should have exactly one selectable direct target", 2057 activity.getAdapter().getSelectableServiceTargetCount(), is(1)); 2058 assertThat("The resolver info must match the resolver info used to create the target", 2059 activity.getAdapter().getItem(0).getResolveInfo(), is(ri)); 2060 2061 // Click on the direct target 2062 String name = serviceTargets.get(0).getTitle().toString(); 2063 onView(withText(name)) 2064 .perform(click()); 2065 waitForIdle(); 2066 2067 FakeEventLog eventLog = getEventLog(activity); 2068 var invocations = eventLog.getShareTargetSelected(); 2069 assertWithMessage("Only one ShareTargetSelected event logged") 2070 .that(invocations).hasSize(1); 2071 FakeEventLog.ShareTargetSelected call = invocations.get(0); 2072 assertWithMessage("targetType should be SELECTION_TYPE_SERVICE") 2073 .that(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE); 2074 assertWithMessage( 2075 "The packages shouldn't match for app target and direct target") 2076 .that(call.getDirectTargetAlsoRanked()).isEqualTo(-1); 2077 } 2078 2079 @Test testWorkTab_displayedWhenWorkProfileUserAvailable()2080 public void testWorkTab_displayedWhenWorkProfileUserAvailable() { 2081 Intent sendIntent = createSendTextIntent(); 2082 sendIntent.setType(TEST_MIME_TYPE); 2083 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2084 2085 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2086 waitForIdle(); 2087 2088 onView(withId(android.R.id.tabs)).check(matches(isDisplayed())); 2089 } 2090 2091 @Test testWorkTab_hiddenWhenWorkProfileUserNotAvailable()2092 public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() { 2093 Intent sendIntent = createSendTextIntent(); 2094 sendIntent.setType(TEST_MIME_TYPE); 2095 2096 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2097 waitForIdle(); 2098 2099 onView(withId(android.R.id.tabs)).check(matches(not(isDisplayed()))); 2100 } 2101 2102 @Test testWorkTab_eachTabUsesExpectedAdapter()2103 public void testWorkTab_eachTabUsesExpectedAdapter() { 2104 int personalProfileTargets = 3; 2105 int otherProfileTargets = 1; 2106 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2107 createResolvedComponentsForTestWithOtherProfile( 2108 personalProfileTargets + otherProfileTargets, /* userID */ 10); 2109 int workProfileTargets = 4; 2110 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest( 2111 workProfileTargets); 2112 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2113 Intent sendIntent = createSendTextIntent(); 2114 sendIntent.setType(TEST_MIME_TYPE); 2115 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2116 2117 final IChooserWrapper activity = (IChooserWrapper) 2118 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2119 waitForIdle(); 2120 2121 assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0)); 2122 onView(withText(R.string.resolver_work_tab)).perform(click()); 2123 assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); 2124 assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets)); 2125 assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets)); 2126 } 2127 2128 @Test testWorkTab_workProfileHasExpectedNumberOfTargets()2129 public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException { 2130 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2131 int workProfileTargets = 4; 2132 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2133 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2134 List<ResolvedComponentInfo> workResolvedComponentInfos = 2135 createResolvedComponentsForTest(workProfileTargets); 2136 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2137 Intent sendIntent = createSendTextIntent(); 2138 sendIntent.setType(TEST_MIME_TYPE); 2139 2140 final IChooserWrapper activity = (IChooserWrapper) 2141 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2142 waitForIdle(); 2143 onView(withText(R.string.resolver_work_tab)).perform(click()); 2144 waitForIdle(); 2145 2146 assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets)); 2147 } 2148 2149 @Test @Ignore testWorkTab_selectingWorkTabAppOpensAppInWorkProfile()2150 public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() { 2151 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2152 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2153 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2154 int workProfileTargets = 4; 2155 List<ResolvedComponentInfo> workResolvedComponentInfos = 2156 createResolvedComponentsForTest(workProfileTargets); 2157 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2158 Intent sendIntent = createSendTextIntent(); 2159 sendIntent.setType(TEST_MIME_TYPE); 2160 ResolveInfo[] chosen = new ResolveInfo[1]; 2161 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 2162 chosen[0] = targetInfo.getResolveInfo(); 2163 return true; 2164 }; 2165 2166 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2167 waitForIdle(); 2168 onView(withText(R.string.resolver_work_tab)).perform(click()); 2169 waitForIdle(); 2170 2171 onView(first(allOf( 2172 withText(workResolvedComponentInfos.get(0) 2173 .getResolveInfoAt(0).activityInfo.applicationInfo.name), 2174 isDisplayed()))) 2175 .perform(click()); 2176 waitForIdle(); 2177 assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0))); 2178 } 2179 2180 @Test testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown()2181 public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() { 2182 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2183 int workProfileTargets = 4; 2184 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2185 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2186 List<ResolvedComponentInfo> workResolvedComponentInfos = 2187 createResolvedComponentsForTest(workProfileTargets); 2188 ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false; 2189 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2190 Intent sendIntent = createSendTextIntent(); 2191 sendIntent.setType(TEST_MIME_TYPE); 2192 2193 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2194 waitForIdle(); 2195 onView(withText(R.string.resolver_work_tab)).perform(click()); 2196 waitForIdle(); 2197 onView(withId(com.android.internal.R.id.contentPanel)) 2198 .perform(swipeUp()); 2199 2200 onView(withText(R.string.resolver_cross_profile_blocked)) 2201 .check(matches(isDisplayed())); 2202 } 2203 2204 @Test testWorkTab_workProfileDisabled_emptyStateShown()2205 public void testWorkTab_workProfileDisabled_emptyStateShown() { 2206 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2207 int workProfileTargets = 4; 2208 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2209 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2210 List<ResolvedComponentInfo> workResolvedComponentInfos = 2211 createResolvedComponentsForTest(workProfileTargets); 2212 mFakeUserRepo.updateState(WORK_USER, false); 2213 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2214 Intent sendIntent = createSendTextIntent(); 2215 sendIntent.setType(TEST_MIME_TYPE); 2216 2217 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2218 waitForIdle(); 2219 onView(withId(com.android.internal.R.id.contentPanel)) 2220 .perform(swipeUp()); 2221 onView(withText(R.string.resolver_work_tab)).perform(click()); 2222 waitForIdle(); 2223 2224 onView(withText(R.string.resolver_turn_on_work_apps)) 2225 .check(matches(isDisplayed())); 2226 } 2227 2228 @Test testWorkTab_noWorkAppsAvailable_emptyStateShown()2229 public void testWorkTab_noWorkAppsAvailable_emptyStateShown() { 2230 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2231 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2232 createResolvedComponentsForTest(3); 2233 List<ResolvedComponentInfo> workResolvedComponentInfos = 2234 createResolvedComponentsForTest(0); 2235 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2236 Intent sendIntent = createSendTextIntent(); 2237 sendIntent.setType(TEST_MIME_TYPE); 2238 2239 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2240 waitForIdle(); 2241 onView(withId(com.android.internal.R.id.contentPanel)) 2242 .perform(swipeUp()); 2243 onView(withText(R.string.resolver_work_tab)).perform(click()); 2244 waitForIdle(); 2245 2246 onView(withText(R.string.resolver_no_work_apps_available)) 2247 .check(matches(isDisplayed())); 2248 } 2249 2250 @Test testWorkTab_previewIsScrollable()2251 public void testWorkTab_previewIsScrollable() { 2252 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2253 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2254 createResolvedComponentsForTest(300); 2255 List<ResolvedComponentInfo> workResolvedComponentInfos = 2256 createResolvedComponentsForTest(3); 2257 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2258 2259 Uri uri = createTestContentProviderUri("image/png", null); 2260 2261 ArrayList<Uri> uris = new ArrayList<>(); 2262 uris.add(uri); 2263 2264 Intent sendIntent = createSendUriIntentWithPreview(uris); 2265 mFakeImageLoader.setBitmap(uri, createWideBitmap()); 2266 2267 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Scrollable preview test")); 2268 waitForIdle(); 2269 2270 onView(withId(com.android.intentresolver.R.id.scrollable_image_preview)) 2271 .check(matches(isDisplayed())); 2272 2273 onView(withId(com.android.internal.R.id.contentPanel)).perform(swipeUp()); 2274 waitForIdle(); 2275 2276 onView(withId(com.android.intentresolver.R.id.chooser_headline_row_container)) 2277 .check(matches(isCompletelyDisplayed())); 2278 onView(withId(com.android.intentresolver.R.id.headline)) 2279 .check(matches(isDisplayed())); 2280 onView(withId(com.android.intentresolver.R.id.scrollable_image_preview)) 2281 .check(matches(not(isDisplayed()))); 2282 } 2283 2284 @Ignore // b/220067877 2285 @Test testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown()2286 public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() { 2287 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2288 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2289 createResolvedComponentsForTest(3); 2290 List<ResolvedComponentInfo> workResolvedComponentInfos = 2291 createResolvedComponentsForTest(0); 2292 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2293 mFakeUserRepo.updateState(WORK_USER, false); 2294 ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false; 2295 Intent sendIntent = createSendTextIntent(); 2296 sendIntent.setType(TEST_MIME_TYPE); 2297 2298 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2299 waitForIdle(); 2300 onView(withId(com.android.internal.R.id.contentPanel)) 2301 .perform(swipeUp()); 2302 onView(withText(R.string.resolver_work_tab)).perform(click()); 2303 waitForIdle(); 2304 2305 onView(withText(R.string.resolver_cross_profile_blocked)) 2306 .check(matches(isDisplayed())); 2307 } 2308 2309 @Test testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown()2310 public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() { 2311 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2312 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2313 createResolvedComponentsForTest(3); 2314 List<ResolvedComponentInfo> workResolvedComponentInfos = 2315 createResolvedComponentsForTest(0); 2316 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2317 mFakeUserRepo.updateState(WORK_USER, false); 2318 Intent sendIntent = createSendTextIntent(); 2319 sendIntent.setType(TEST_MIME_TYPE); 2320 2321 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2322 waitForIdle(); 2323 onView(withId(com.android.internal.R.id.contentPanel)) 2324 .perform(swipeUp()); 2325 onView(withText(R.string.resolver_work_tab)).perform(click()); 2326 waitForIdle(); 2327 2328 onView(withText(R.string.resolver_no_work_apps_available)) 2329 .check(matches(isDisplayed())); 2330 } 2331 2332 @Test @Ignore("b/222124533") testAppTargetLogging()2333 public void testAppTargetLogging() throws InterruptedException { 2334 Intent sendIntent = createSendTextIntent(); 2335 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 2336 2337 setupResolverControllers(resolvedComponentInfos); 2338 2339 final IChooserWrapper activity = (IChooserWrapper) 2340 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 2341 waitForIdle(); 2342 2343 // TODO(b/222124533): other test cases use a timeout to make sure that the UI is fully 2344 // populated; without one, this test flakes. Ideally we should address the need for a 2345 // timeout everywhere instead of introducing one to fix this particular test. 2346 2347 assertThat(activity.getAdapter().getCount(), is(2)); 2348 onView(withId(com.android.internal.R.id.profile_button)).check(doesNotExist()); 2349 2350 ResolveInfo[] chosen = new ResolveInfo[1]; 2351 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 2352 chosen[0] = targetInfo.getResolveInfo(); 2353 return true; 2354 }; 2355 2356 ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0); 2357 onView(withText(toChoose.activityInfo.name)) 2358 .perform(click()); 2359 waitForIdle(); 2360 2361 // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events. 2362 } 2363 2364 @Test testDirectTargetLogging()2365 public void testDirectTargetLogging() { 2366 Intent sendIntent = createSendTextIntent(); 2367 // We need app targets for direct targets to get displayed 2368 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 2369 setupResolverControllers(resolvedComponentInfos); 2370 2371 // create test shortcut loader factory, remember loaders and their callbacks 2372 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 2373 new SparseArray<>(); 2374 ChooserActivityOverrideData.getInstance().shortcutLoaderFactory = 2375 (userHandle, callback) -> { 2376 Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>> pair = 2377 new Pair<>(mock(ShortcutLoader.class), callback); 2378 shortcutLoaders.put(userHandle.getIdentifier(), pair); 2379 return pair.first; 2380 }; 2381 2382 // Start activity 2383 ChooserWrapperActivity activity = 2384 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 2385 waitForIdle(); 2386 2387 // verify that ShortcutLoader was queried 2388 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 2389 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 2390 verify(shortcutLoaders.get(0).first, times(1)) 2391 .updateAppTargets(appTargets.capture()); 2392 2393 // send shortcuts 2394 assertThat( 2395 "Wrong number of app targets", 2396 appTargets.getValue().length, 2397 is(resolvedComponentInfos.size())); 2398 List<ChooserTarget> serviceTargets = createDirectShareTargets(1, 2399 resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName); 2400 ShortcutLoader.Result result = new ShortcutLoader.Result( 2401 // TODO: test another value as well 2402 false, 2403 appTargets.getValue(), 2404 new ShortcutLoader.ShortcutResultInfo[] { 2405 new ShortcutLoader.ShortcutResultInfo( 2406 appTargets.getValue()[0], 2407 serviceTargets 2408 ) 2409 }, 2410 new HashMap<>(), 2411 new HashMap<>() 2412 ); 2413 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 2414 waitForIdle(); 2415 2416 assertThat("Chooser should have 3 targets (2 apps, 1 direct)", 2417 activity.getAdapter().getCount(), is(3)); 2418 assertThat("Chooser should have exactly one selectable direct target", 2419 activity.getAdapter().getSelectableServiceTargetCount(), is(1)); 2420 assertThat( 2421 "The resolver info must match the resolver info used to create the target", 2422 activity.getAdapter().getItem(0).getResolveInfo(), 2423 is(resolvedComponentInfos.get(0).getResolveInfoAt(0))); 2424 2425 // Click on the direct target 2426 String name = serviceTargets.get(0).getTitle().toString(); 2427 onView(withText(name)) 2428 .perform(click()); 2429 waitForIdle(); 2430 2431 FakeEventLog eventLog = getEventLog(activity); 2432 assertThat(eventLog.getShareTargetSelected()).hasSize(1); 2433 FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0); 2434 assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE); 2435 } 2436 2437 @Test testDirectTargetPinningDialog()2438 public void testDirectTargetPinningDialog() { 2439 Intent sendIntent = createSendTextIntent(); 2440 // We need app targets for direct targets to get displayed 2441 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 2442 setupResolverControllers(resolvedComponentInfos); 2443 2444 // create test shortcut loader factory, remember loaders and their callbacks 2445 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 2446 new SparseArray<>(); 2447 ChooserActivityOverrideData.getInstance().shortcutLoaderFactory = 2448 (userHandle, callback) -> { 2449 Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>> pair = 2450 new Pair<>(mock(ShortcutLoader.class), callback); 2451 shortcutLoaders.put(userHandle.getIdentifier(), pair); 2452 return pair.first; 2453 }; 2454 2455 // Start activity 2456 final IChooserWrapper activity = (IChooserWrapper) 2457 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 2458 waitForIdle(); 2459 2460 // verify that ShortcutLoader was queried 2461 ArgumentCaptor<DisplayResolveInfo[]> appTargets = 2462 ArgumentCaptor.forClass(DisplayResolveInfo[].class); 2463 verify(shortcutLoaders.get(0).first, times(1)) 2464 .updateAppTargets(appTargets.capture()); 2465 2466 // send shortcuts 2467 List<ChooserTarget> serviceTargets = createDirectShareTargets( 2468 1, 2469 resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName); 2470 ShortcutLoader.Result result = new ShortcutLoader.Result( 2471 // TODO: test another value as well 2472 false, 2473 appTargets.getValue(), 2474 new ShortcutLoader.ShortcutResultInfo[] { 2475 new ShortcutLoader.ShortcutResultInfo( 2476 appTargets.getValue()[0], 2477 serviceTargets 2478 ) 2479 }, 2480 new HashMap<>(), 2481 new HashMap<>() 2482 ); 2483 activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result)); 2484 waitForIdle(); 2485 2486 // Long-click on the direct target 2487 String name = serviceTargets.get(0).getTitle().toString(); 2488 onView(withText(name)).perform(longClick()); 2489 waitForIdle(); 2490 2491 onView(withId(R.id.chooser_dialog_content)).check(matches(isDisplayed())); 2492 } 2493 2494 @Test @Ignore testEmptyDirectRowLogging()2495 public void testEmptyDirectRowLogging() throws InterruptedException { 2496 Intent sendIntent = createSendTextIntent(); 2497 // We need app targets for direct targets to get displayed 2498 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 2499 setupResolverControllers(resolvedComponentInfos); 2500 2501 // Start activity 2502 final IChooserWrapper activity = (IChooserWrapper) 2503 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 2504 2505 // Thread.sleep shouldn't be a thing in an integration test but it's 2506 // necessary here because of the way the code is structured 2507 Thread.sleep(3000); 2508 2509 assertThat("Chooser should have 2 app targets", 2510 activity.getAdapter().getCount(), is(2)); 2511 assertThat("Chooser should have no direct targets", 2512 activity.getAdapter().getSelectableServiceTargetCount(), is(0)); 2513 2514 // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events. 2515 } 2516 2517 @Ignore // b/220067877 2518 @Test testCopyTextToClipboardLogging()2519 public void testCopyTextToClipboardLogging() throws Exception { 2520 Intent sendIntent = createSendTextIntent(); 2521 List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); 2522 2523 setupResolverControllers(resolvedComponentInfos); 2524 2525 final IChooserWrapper activity = (IChooserWrapper) 2526 mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); 2527 waitForIdle(); 2528 2529 onView(withId(com.android.internal.R.id.chooser_copy_button)).check(matches(isDisplayed())); 2530 onView(withId(com.android.internal.R.id.chooser_copy_button)).perform(click()); 2531 2532 // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events. 2533 } 2534 2535 @Test @Ignore("b/222124533") testSwitchProfileLogging()2536 public void testSwitchProfileLogging() throws InterruptedException { 2537 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2538 int workProfileTargets = 4; 2539 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2540 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2541 List<ResolvedComponentInfo> workResolvedComponentInfos = 2542 createResolvedComponentsForTest(workProfileTargets); 2543 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2544 Intent sendIntent = createSendTextIntent(); 2545 sendIntent.setType(TEST_MIME_TYPE); 2546 2547 final IChooserWrapper activity = (IChooserWrapper) 2548 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2549 waitForIdle(); 2550 onView(withText(R.string.resolver_work_tab)).perform(click()); 2551 waitForIdle(); 2552 onView(withText(R.string.resolver_personal_tab)).perform(click()); 2553 waitForIdle(); 2554 2555 // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events. 2556 } 2557 2558 @Test testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_doesNotAutoLaunch()2559 public void testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_doesNotAutoLaunch() { 2560 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2561 int workProfileTargets = 4; 2562 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2563 createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10); 2564 List<ResolvedComponentInfo> workResolvedComponentInfos = 2565 createResolvedComponentsForTest(workProfileTargets); 2566 ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false; 2567 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2568 Intent sendIntent = createSendTextIntent(); 2569 ResolveInfo[] chosen = new ResolveInfo[1]; 2570 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 2571 chosen[0] = targetInfo.getResolveInfo(); 2572 return true; 2573 }; 2574 2575 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test")); 2576 waitForIdle(); 2577 2578 assertNull(chosen[0]); 2579 } 2580 2581 @Test testOneInitialIntent_noAutolaunch()2582 public void testOneInitialIntent_noAutolaunch() { 2583 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2584 createResolvedComponentsForTest(1); 2585 setupResolverControllers(personalResolvedComponentInfos); 2586 Intent chooserIntent = createChooserIntent(createSendTextIntent(), 2587 new Intent[] {new Intent("action.fake")}); 2588 ResolveInfo[] chosen = new ResolveInfo[1]; 2589 ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> { 2590 chosen[0] = targetInfo.getResolveInfo(); 2591 return true; 2592 }; 2593 mPackageManager = createFakePackageManager(createFakeResolveInfo()); 2594 waitForIdle(); 2595 2596 IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent); 2597 waitForIdle(); 2598 2599 assertNull(chosen[0]); 2600 assertThat(activity 2601 .getPersonalListAdapter().getCallerTargetCount(), is(1)); 2602 } 2603 2604 @Test testWorkTab_withInitialIntents_workTabDoesNotIncludePersonalInitialIntents()2605 public void testWorkTab_withInitialIntents_workTabDoesNotIncludePersonalInitialIntents() { 2606 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2607 int workProfileTargets = 1; 2608 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2609 createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10); 2610 List<ResolvedComponentInfo> workResolvedComponentInfos = 2611 createResolvedComponentsForTest(workProfileTargets); 2612 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2613 Intent[] initialIntents = { 2614 new Intent("action.fake1"), 2615 new Intent("action.fake2") 2616 }; 2617 Intent chooserIntent = createChooserIntent(createSendTextIntent(), initialIntents); 2618 mPackageManager = createFakePackageManager(createFakeResolveInfo()); 2619 waitForIdle(); 2620 2621 IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent); 2622 waitForIdle(); 2623 2624 assertThat(activity.getPersonalListAdapter().getCallerTargetCount(), is(2)); 2625 assertThat(activity.getWorkListAdapter().getCallerTargetCount(), is(0)); 2626 } 2627 2628 @Test testWorkTab_xProfileIntentsDisabled_personalToWork_nonSendIntent_emptyStateShown()2629 public void testWorkTab_xProfileIntentsDisabled_personalToWork_nonSendIntent_emptyStateShown() { 2630 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2631 int workProfileTargets = 4; 2632 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2633 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2634 List<ResolvedComponentInfo> workResolvedComponentInfos = 2635 createResolvedComponentsForTest(workProfileTargets); 2636 ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false; 2637 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2638 Intent[] initialIntents = { 2639 new Intent("action.fake1"), 2640 new Intent("action.fake2") 2641 }; 2642 Intent chooserIntent = createChooserIntent(new Intent(), initialIntents); 2643 mPackageManager = createFakePackageManager(createFakeResolveInfo()); 2644 2645 2646 mActivityRule.launchActivity(chooserIntent); 2647 waitForIdle(); 2648 onView(withText(R.string.resolver_work_tab)).perform(click()); 2649 waitForIdle(); 2650 onView(withId(com.android.internal.R.id.contentPanel)) 2651 .perform(swipeUp()); 2652 2653 onView(withText(R.string.resolver_cross_profile_blocked)) 2654 .check(matches(isDisplayed())); 2655 } 2656 2657 @Test testWorkTab_noWorkAppsAvailable_nonSendIntent_emptyStateShown()2658 public void testWorkTab_noWorkAppsAvailable_nonSendIntent_emptyStateShown() { 2659 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2660 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2661 createResolvedComponentsForTest(3); 2662 List<ResolvedComponentInfo> workResolvedComponentInfos = 2663 createResolvedComponentsForTest(0); 2664 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2665 Intent[] initialIntents = { 2666 new Intent("action.fake1"), 2667 new Intent("action.fake2") 2668 }; 2669 Intent chooserIntent = createChooserIntent(new Intent(), initialIntents); 2670 mPackageManager = createFakePackageManager(createFakeResolveInfo()); 2671 2672 2673 mActivityRule.launchActivity(chooserIntent); 2674 waitForIdle(); 2675 onView(withId(com.android.internal.R.id.contentPanel)) 2676 .perform(swipeUp()); 2677 onView(withText(R.string.resolver_work_tab)).perform(click()); 2678 waitForIdle(); 2679 2680 onView(withText(R.string.resolver_no_work_apps_available)) 2681 .check(matches(isDisplayed())); 2682 } 2683 2684 @Test testDeduplicateCallerTargetRankedTarget()2685 public void testDeduplicateCallerTargetRankedTarget() { 2686 // Create 4 ranked app targets. 2687 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2688 createResolvedComponentsForTest(4); 2689 setupResolverControllers(personalResolvedComponentInfos); 2690 // Create caller target which is duplicate with one of app targets 2691 Intent chooserIntent = createChooserIntent(createSendTextIntent(), 2692 new Intent[] {new Intent("action.fake")}); 2693 mPackageManager = createFakePackageManager(ResolverDataProvider.createResolveInfo(0, 2694 UserHandle.USER_CURRENT, PERSONAL_USER_HANDLE)); 2695 waitForIdle(); 2696 2697 IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent); 2698 waitForIdle(); 2699 2700 // Total 4 targets (1 caller target, 3 ranked targets) 2701 assertThat(activity.getAdapter().getCount(), is(4)); 2702 assertThat(activity.getAdapter().getCallerTargetCount(), is(1)); 2703 assertThat(activity.getAdapter().getRankedTargetCount(), is(3)); 2704 } 2705 2706 @Test test_query_shortcut_loader_for_the_selected_tab()2707 public void test_query_shortcut_loader_for_the_selected_tab() { 2708 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false); 2709 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2710 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); 2711 List<ResolvedComponentInfo> workResolvedComponentInfos = 2712 createResolvedComponentsForTest(3); 2713 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2714 ShortcutLoader personalProfileShortcutLoader = mock(ShortcutLoader.class); 2715 ShortcutLoader workProfileShortcutLoader = mock(ShortcutLoader.class); 2716 final SparseArray<ShortcutLoader> shortcutLoaders = new SparseArray<>(); 2717 shortcutLoaders.put(0, personalProfileShortcutLoader); 2718 shortcutLoaders.put(10, workProfileShortcutLoader); 2719 ChooserActivityOverrideData.getInstance().shortcutLoaderFactory = 2720 (userHandle, callback) -> shortcutLoaders.get(userHandle.getIdentifier(), null); 2721 Intent sendIntent = createSendTextIntent(); 2722 sendIntent.setType(TEST_MIME_TYPE); 2723 2724 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); 2725 waitForIdle(); 2726 onView(withId(com.android.internal.R.id.contentPanel)) 2727 .perform(swipeUp()); 2728 waitForIdle(); 2729 2730 verify(personalProfileShortcutLoader, times(1)).updateAppTargets(any()); 2731 2732 onView(withText(R.string.resolver_work_tab)).perform(click()); 2733 waitForIdle(); 2734 2735 verify(workProfileShortcutLoader, times(1)).updateAppTargets(any()); 2736 } 2737 2738 @Test testClonedProfilePresent_personalAdapterIsSetWithPersonalProfile()2739 public void testClonedProfilePresent_personalAdapterIsSetWithPersonalProfile() { 2740 // enable cloneProfile 2741 markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true); 2742 List<ResolvedComponentInfo> resolvedComponentInfos = 2743 createResolvedComponentsWithCloneProfileForTest( 2744 3, 2745 PERSONAL_USER_HANDLE, 2746 CLONE_PROFILE_USER_HANDLE); 2747 setupResolverControllers(resolvedComponentInfos); 2748 Intent sendIntent = createSendTextIntent(); 2749 2750 final IChooserWrapper activity = (IChooserWrapper) mActivityRule 2751 .launchActivity(Intent.createChooser(sendIntent, "personalProfileTest")); 2752 waitForIdle(); 2753 2754 assertThat(activity.getPersonalListAdapter().getUserHandle(), is(PERSONAL_USER_HANDLE)); 2755 assertThat(activity.getAdapter().getCount(), is(3)); 2756 } 2757 2758 @Test testClonedProfilePresent_personalTabUsesExpectedAdapter()2759 public void testClonedProfilePresent_personalTabUsesExpectedAdapter() { 2760 markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true); 2761 List<ResolvedComponentInfo> personalResolvedComponentInfos = 2762 createResolvedComponentsForTest(3); 2763 List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest( 2764 4); 2765 setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); 2766 Intent sendIntent = createSendTextIntent(); 2767 sendIntent.setType(TEST_MIME_TYPE); 2768 2769 2770 final IChooserWrapper activity = (IChooserWrapper) 2771 mActivityRule.launchActivity(Intent.createChooser(sendIntent, "multi tab test")); 2772 waitForIdle(); 2773 2774 assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE)); 2775 } 2776 2777 @Test chooserDisabledWhileDeviceFrpLocked()2778 public void chooserDisabledWhileDeviceFrpLocked() { 2779 mGlobalSettings.putBoolean(Settings.Global.SECURE_FRP_MODE, true); 2780 Intent viewIntent = createSendTextIntent(); 2781 ChooserWrapperActivity activity = mActivityRule.launchActivity( 2782 Intent.createChooser(viewIntent, "chooser test")); 2783 waitForIdle(); 2784 assertTrue(activity.isFinishing()); 2785 } 2786 createChooserIntent(Intent intent, Intent[] initialIntents)2787 private Intent createChooserIntent(Intent intent, Intent[] initialIntents) { 2788 Intent chooserIntent = new Intent(); 2789 chooserIntent.setAction(Intent.ACTION_CHOOSER); 2790 chooserIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending"); 2791 chooserIntent.putExtra(Intent.EXTRA_TITLE, "some title"); 2792 chooserIntent.putExtra(Intent.EXTRA_INTENT, intent); 2793 chooserIntent.setType("text/plain"); 2794 if (initialIntents != null) { 2795 chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents); 2796 } 2797 return chooserIntent; 2798 } 2799 2800 /* This is a "test of a test" to make sure that our inherited test class 2801 * is successfully configured to operate on the unbundled-equivalent 2802 * ChooserWrapperActivity. 2803 * 2804 * TODO: remove after unbundling is complete. 2805 */ 2806 @Test testWrapperActivityHasExpectedConcreteType()2807 public void testWrapperActivityHasExpectedConcreteType() { 2808 final ChooserActivity activity = mActivityRule.launchActivity( 2809 Intent.createChooser(new Intent("ACTION_FOO"), "foo")); 2810 waitForIdle(); 2811 assertThat(activity).isInstanceOf(ChooserWrapperActivity.class); 2812 } 2813 createFakeResolveInfo()2814 private ResolveInfo createFakeResolveInfo() { 2815 ResolveInfo ri = new ResolveInfo(); 2816 ri.activityInfo = new ActivityInfo(); 2817 ri.activityInfo.name = "FakeActivityName"; 2818 ri.activityInfo.packageName = "fake.package.name"; 2819 ri.activityInfo.applicationInfo = new ApplicationInfo(); 2820 ri.activityInfo.applicationInfo.packageName = "fake.package.name"; 2821 ri.userHandle = UserHandle.CURRENT; 2822 return ri; 2823 } 2824 createSendTextIntent()2825 private Intent createSendTextIntent() { 2826 Intent sendIntent = new Intent(); 2827 sendIntent.setAction(Intent.ACTION_SEND); 2828 sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending"); 2829 sendIntent.setType("text/plain"); 2830 return sendIntent; 2831 } 2832 createSendImageIntent(Uri imageThumbnail)2833 private Intent createSendImageIntent(Uri imageThumbnail) { 2834 Intent sendIntent = new Intent(); 2835 sendIntent.setAction(Intent.ACTION_SEND); 2836 sendIntent.putExtra(Intent.EXTRA_STREAM, imageThumbnail); 2837 sendIntent.setType("image/png"); 2838 if (imageThumbnail != null) { 2839 ClipData.Item clipItem = new ClipData.Item(imageThumbnail); 2840 sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem)); 2841 } 2842 2843 return sendIntent; 2844 } 2845 createTestContentProviderUri( @ullable String mimeType, @Nullable String streamType)2846 private Uri createTestContentProviderUri( 2847 @Nullable String mimeType, @Nullable String streamType) { 2848 return createTestContentProviderUri(mimeType, streamType, 0); 2849 } 2850 createTestContentProviderUri( @ullable String mimeType, @Nullable String streamType, long streamTypeTimeout)2851 private Uri createTestContentProviderUri( 2852 @Nullable String mimeType, @Nullable String streamType, long streamTypeTimeout) { 2853 String packageName = 2854 InstrumentationRegistry.getInstrumentation().getContext().getPackageName(); 2855 Uri.Builder builder = Uri.parse("content://" + packageName + "/image.png") 2856 .buildUpon(); 2857 if (mimeType != null) { 2858 builder.appendQueryParameter(TestContentProvider.PARAM_MIME_TYPE, mimeType); 2859 } 2860 if (streamType != null) { 2861 builder.appendQueryParameter(TestContentProvider.PARAM_STREAM_TYPE, streamType); 2862 } 2863 if (streamTypeTimeout > 0) { 2864 builder.appendQueryParameter( 2865 TestContentProvider.PARAM_STREAM_TYPE_TIMEOUT, 2866 Long.toString(streamTypeTimeout)); 2867 } 2868 return builder.build(); 2869 } 2870 createSendTextIntentWithPreview(String title, Uri imageThumbnail)2871 private Intent createSendTextIntentWithPreview(String title, Uri imageThumbnail) { 2872 Intent sendIntent = new Intent(); 2873 sendIntent.setAction(Intent.ACTION_SEND); 2874 sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending"); 2875 sendIntent.putExtra(Intent.EXTRA_TITLE, title); 2876 if (imageThumbnail != null) { 2877 ClipData.Item clipItem = new ClipData.Item(imageThumbnail); 2878 sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem)); 2879 } 2880 2881 return sendIntent; 2882 } 2883 createSendUriIntentWithPreview(ArrayList<Uri> uris)2884 private Intent createSendUriIntentWithPreview(ArrayList<Uri> uris) { 2885 Intent sendIntent = new Intent(); 2886 2887 if (uris.size() > 1) { 2888 sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE); 2889 sendIntent.putExtra(Intent.EXTRA_STREAM, uris); 2890 } else { 2891 sendIntent.setAction(Intent.ACTION_SEND); 2892 sendIntent.putExtra(Intent.EXTRA_STREAM, uris.get(0)); 2893 } 2894 2895 return sendIntent; 2896 } 2897 createViewTextIntent()2898 private Intent createViewTextIntent() { 2899 Intent viewIntent = new Intent(); 2900 viewIntent.setAction(Intent.ACTION_VIEW); 2901 viewIntent.putExtra(Intent.EXTRA_TEXT, "testing intent viewing"); 2902 return viewIntent; 2903 } 2904 createResolvedComponentsForTest(int numberOfResults)2905 private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) { 2906 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 2907 for (int i = 0; i < numberOfResults; i++) { 2908 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, PERSONAL_USER_HANDLE)); 2909 } 2910 return infoList; 2911 } 2912 createResolvedComponentsWithCloneProfileForTest( int numberOfResults, UserHandle resolvedForPersonalUser, UserHandle resolvedForClonedUser)2913 private List<ResolvedComponentInfo> createResolvedComponentsWithCloneProfileForTest( 2914 int numberOfResults, 2915 UserHandle resolvedForPersonalUser, 2916 UserHandle resolvedForClonedUser) { 2917 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 2918 for (int i = 0; i < 1; i++) { 2919 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, 2920 resolvedForPersonalUser)); 2921 } 2922 for (int i = 1; i < numberOfResults; i++) { 2923 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, 2924 resolvedForClonedUser)); 2925 } 2926 return infoList; 2927 } 2928 createResolvedComponentsForTestWithOtherProfile( int numberOfResults)2929 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( 2930 int numberOfResults) { 2931 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 2932 for (int i = 0; i < numberOfResults; i++) { 2933 if (i == 0) { 2934 infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, 2935 PERSONAL_USER_HANDLE)); 2936 } else { 2937 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, 2938 PERSONAL_USER_HANDLE)); 2939 } 2940 } 2941 return infoList; 2942 } 2943 createResolvedComponentsForTestWithOtherProfile( int numberOfResults, int userId)2944 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( 2945 int numberOfResults, int userId) { 2946 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 2947 for (int i = 0; i < numberOfResults; i++) { 2948 if (i == 0) { 2949 infoList.add( 2950 ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId, 2951 PERSONAL_USER_HANDLE)); 2952 } else { 2953 infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, 2954 PERSONAL_USER_HANDLE)); 2955 } 2956 } 2957 return infoList; 2958 } 2959 createResolvedComponentsForTestWithUserId( int numberOfResults, int userId)2960 private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId( 2961 int numberOfResults, int userId) { 2962 List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); 2963 for (int i = 0; i < numberOfResults; i++) { 2964 infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId, 2965 PERSONAL_USER_HANDLE)); 2966 } 2967 return infoList; 2968 } 2969 createDirectShareTargets(int numberOfResults, String packageName)2970 private List<ChooserTarget> createDirectShareTargets(int numberOfResults, String packageName) { 2971 Icon icon = Icon.createWithBitmap(createBitmap()); 2972 String testTitle = "testTitle"; 2973 List<ChooserTarget> targets = new ArrayList<>(); 2974 for (int i = 0; i < numberOfResults; i++) { 2975 ComponentName componentName; 2976 if (packageName.isEmpty()) { 2977 componentName = ResolverDataProvider.createComponentName(i); 2978 } else { 2979 componentName = new ComponentName(packageName, packageName + ".class"); 2980 } 2981 ChooserTarget tempTarget = new ChooserTarget( 2982 testTitle + i, 2983 icon, 2984 (float) (1 - ((i + 1) / 10.0)), 2985 componentName, 2986 null); 2987 targets.add(tempTarget); 2988 } 2989 return targets; 2990 } 2991 waitForIdle()2992 private void waitForIdle() { 2993 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 2994 } 2995 createBitmap()2996 private Bitmap createBitmap() { 2997 return createBitmap(200, 200); 2998 } 2999 createWideBitmap()3000 private Bitmap createWideBitmap() { 3001 return createWideBitmap(Color.RED); 3002 } 3003 createWideBitmap(int bgColor)3004 private Bitmap createWideBitmap(int bgColor) { 3005 WindowManager windowManager = InstrumentationRegistry.getInstrumentation() 3006 .getTargetContext() 3007 .getSystemService(WindowManager.class); 3008 int width = 3000; 3009 if (windowManager != null) { 3010 Rect bounds = windowManager.getMaximumWindowMetrics().getBounds(); 3011 width = bounds.width() + 200; 3012 } 3013 return createBitmap(width, 100, bgColor); 3014 } 3015 createBitmap(int width, int height)3016 private Bitmap createBitmap(int width, int height) { 3017 return createBitmap(width, height, Color.RED); 3018 } 3019 createBitmap(int width, int height, int bgColor)3020 private Bitmap createBitmap(int width, int height, int bgColor) { 3021 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 3022 Canvas canvas = new Canvas(bitmap); 3023 3024 Paint paint = new Paint(); 3025 paint.setColor(bgColor); 3026 paint.setStyle(Paint.Style.FILL); 3027 canvas.drawPaint(paint); 3028 3029 paint.setColor(Color.WHITE); 3030 paint.setAntiAlias(true); 3031 paint.setTextSize(14.f); 3032 paint.setTextAlign(Paint.Align.CENTER); 3033 canvas.drawText("Hi!", (width / 2.f), (height / 2.f), paint); 3034 3035 return bitmap; 3036 } 3037 createShortcuts(Context context)3038 private List<ShareShortcutInfo> createShortcuts(Context context) { 3039 Intent testIntent = new Intent("TestIntent"); 3040 3041 List<ShareShortcutInfo> shortcuts = new ArrayList<>(); 3042 shortcuts.add(new ShareShortcutInfo( 3043 new ShortcutInfo.Builder(context, "shortcut1") 3044 .setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2 3045 new ComponentName("package1", "class1"))); 3046 shortcuts.add(new ShareShortcutInfo( 3047 new ShortcutInfo.Builder(context, "shortcut2") 3048 .setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3 3049 new ComponentName("package2", "class2"))); 3050 shortcuts.add(new ShareShortcutInfo( 3051 new ShortcutInfo.Builder(context, "shortcut3") 3052 .setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0 3053 new ComponentName("package3", "class3"))); 3054 shortcuts.add(new ShareShortcutInfo( 3055 new ShortcutInfo.Builder(context, "shortcut4") 3056 .setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2 3057 new ComponentName("package4", "class4"))); 3058 3059 return shortcuts; 3060 } 3061 markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable)3062 private void markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable) { 3063 if (workAvailable) { 3064 mFakeUserRepo.addUser(WORK_USER, /* available= */ true); 3065 } 3066 if (cloneAvailable) { 3067 mFakeUserRepo.addUser(CLONE_USER, /* available= */ true); 3068 } 3069 } 3070 setupResolverControllers( List<ResolvedComponentInfo> personalResolvedComponentInfos)3071 private void setupResolverControllers( 3072 List<ResolvedComponentInfo> personalResolvedComponentInfos) { 3073 setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>()); 3074 } 3075 setupResolverControllers( List<ResolvedComponentInfo> personalResolvedComponentInfos, List<ResolvedComponentInfo> workResolvedComponentInfos)3076 private void setupResolverControllers( 3077 List<ResolvedComponentInfo> personalResolvedComponentInfos, 3078 List<ResolvedComponentInfo> workResolvedComponentInfos) { 3079 when( 3080 ChooserActivityOverrideData 3081 .getInstance() 3082 .resolverListController 3083 .getResolversForIntentAsUser( 3084 Mockito.anyBoolean(), 3085 Mockito.anyBoolean(), 3086 Mockito.anyBoolean(), 3087 Mockito.isA(List.class), 3088 eq(PERSONAL_USER_HANDLE))) 3089 .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); 3090 when( 3091 ChooserActivityOverrideData 3092 .getInstance() 3093 .workResolverListController 3094 .getResolversForIntentAsUser( 3095 Mockito.anyBoolean(), 3096 Mockito.anyBoolean(), 3097 Mockito.anyBoolean(), 3098 Mockito.isA(List.class), 3099 eq(WORK_PROFILE_USER_HANDLE))) 3100 .thenReturn(new ArrayList<>(workResolvedComponentInfos)); 3101 } 3102 withGridColumnCount(int columnCount)3103 private static GridRecyclerSpanCountMatcher withGridColumnCount(int columnCount) { 3104 return new GridRecyclerSpanCountMatcher(Matchers.is(columnCount)); 3105 } 3106 3107 private static class GridRecyclerSpanCountMatcher extends 3108 BoundedDiagnosingMatcher<View, RecyclerView> { 3109 3110 private final Matcher<Integer> mIntegerMatcher; 3111 GridRecyclerSpanCountMatcher(Matcher<Integer> integerMatcher)3112 private GridRecyclerSpanCountMatcher(Matcher<Integer> integerMatcher) { 3113 super(RecyclerView.class); 3114 this.mIntegerMatcher = integerMatcher; 3115 } 3116 3117 @Override describeMoreTo(Description description)3118 protected void describeMoreTo(Description description) { 3119 description.appendText("RecyclerView grid layout span count to match: "); 3120 this.mIntegerMatcher.describeTo(description); 3121 } 3122 3123 @Override matchesSafely(RecyclerView view, Description mismatchDescription)3124 protected boolean matchesSafely(RecyclerView view, Description mismatchDescription) { 3125 int spanCount = ((GridLayoutManager) view.getLayoutManager()).getSpanCount(); 3126 if (this.mIntegerMatcher.matches(spanCount)) { 3127 return true; 3128 } else { 3129 mismatchDescription.appendText("RecyclerView grid layout span count was ") 3130 .appendValue(spanCount); 3131 return false; 3132 } 3133 } 3134 } 3135 givenAppTargets(int appCount)3136 private void givenAppTargets(int appCount) { 3137 List<ResolvedComponentInfo> resolvedComponentInfos = 3138 createResolvedComponentsForTest(appCount); 3139 setupResolverControllers(resolvedComponentInfos); 3140 } 3141 updateMaxTargetsPerRowResource(int targetsPerRow)3142 private void updateMaxTargetsPerRowResource(int targetsPerRow) { 3143 Resources resources = Mockito.spy( 3144 InstrumentationRegistry.getInstrumentation().getContext().getResources()); 3145 ChooserActivityOverrideData.getInstance().resources = resources; 3146 doReturn(targetsPerRow).when(resources).getInteger( 3147 R.integer.config_chooser_max_targets_per_row); 3148 } 3149 3150 private SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> createShortcutLoaderFactory()3151 createShortcutLoaderFactory() { 3152 SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders = 3153 new SparseArray<>(); 3154 ChooserActivityOverrideData.getInstance().shortcutLoaderFactory = 3155 (userHandle, callback) -> { 3156 Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>> pair = 3157 new Pair<>(mock(ShortcutLoader.class), callback); 3158 shortcutLoaders.put(userHandle.getIdentifier(), pair); 3159 return pair.first; 3160 }; 3161 return shortcutLoaders; 3162 } 3163 } 3164