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