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