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