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