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