diff options
5 files changed, 208 insertions, 28 deletions
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index c53516389cd9..9fc0da83c504 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -300,21 +300,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) { UserHandle listUserHandle = activeListAdapter.getUserHandle(); - if (listUserHandle.equals(mWorkProfileUserHandle) - && mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) { - DevicePolicyEventLogger - .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED) - .setStrings(getMetricsCategory()) - .write(); - showWorkProfileOffEmptyState(activeListAdapter, - v -> { - ProfileDescriptor descriptor = getItem( - userHandleToPageIndex(activeListAdapter.getUserHandle())); - showSpinner(descriptor.getEmptyStateView()); - mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle); - }); - return false; - } + if (UserHandle.myUserId() != listUserHandle.getIdentifier()) { if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(), UserHandle.myUserId(), listUserHandle.getIdentifier())) { @@ -352,7 +338,65 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { protected abstract void showNoWorkToPersonalIntentsEmptyState( ResolverListAdapter activeListAdapter); - void showNoAppsAvailableEmptyState(ResolverListAdapter listAdapter) { + /** + * The empty state screens are shown according to their priority: + * <ol> + * <li>(highest priority) cross-profile disabled by policy (handled in + * {@link #rebuildTab(ResolverListAdapter, boolean)})</li> + * <li>no apps available</li> + * <li>(least priority) work is off</li> + * </ol> + * + * The intention is to prevent the user from having to turn + * the work profile on if there will not be any apps resolved + * anyway. + */ + void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) { + if (maybeShowWorkProfileOffEmptyState(listAdapter)) { + return; + } + maybeShowNoAppsAvailableEmptyState(listAdapter); + } + + /** + * Returns {@code true} if the work profile off empty state screen is shown. + */ + private boolean maybeShowWorkProfileOffEmptyState(ResolverListAdapter listAdapter) { + UserHandle listUserHandle = listAdapter.getUserHandle(); + if (!listUserHandle.equals(mWorkProfileUserHandle) + || !mInjector.isQuietModeEnabled(mWorkProfileUserHandle) + || !hasResolvedAppsInWorkProfile(listAdapter)) { + return false; + } + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED) + .setStrings(getMetricsCategory()) + .write(); + showWorkProfileOffEmptyState(listAdapter, + v -> { + ProfileDescriptor descriptor = getItem( + userHandleToPageIndex(listAdapter.getUserHandle())); + showSpinner(descriptor.getEmptyStateView()); + mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle); + }); + return true; + } + + /** + * Returns {@code true} if there is at least one app resolved in the work profile, + * regardless of whether the work profile is enabled or not. + */ + private boolean hasResolvedAppsInWorkProfile(ResolverListAdapter listAdapter) { + List<ResolverActivity.ResolvedComponentInfo> userStateIndependentWorkResolvers = + listAdapter.mResolverListController.getUserStateIndependentResolversAsUser( + listAdapter.getIntents(), mWorkProfileUserHandle); + return userStateIndependentWorkResolvers.stream() + .anyMatch(resolvedComponentInfo -> + resolvedComponentInfo.getResolveInfoAt(0).targetUserId + == UserHandle.USER_CURRENT); + } + + private void maybeShowNoAppsAvailableEmptyState(ResolverListAdapter listAdapter) { UserHandle listUserHandle = listAdapter.getUserHandle(); if (mWorkProfileUserHandle != null && (UserHandle.myUserId() == listUserHandle.getIdentifier() diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 2352180bcba3..f088ab38658c 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -994,8 +994,8 @@ public class ResolverActivity extends Activity implements if (isAutolaunching() || maybeAutolaunchActivity()) { return; } - if (shouldShowEmptyState(listAdapter)) { - mMultiProfilePagerAdapter.showNoAppsAvailableEmptyState(listAdapter); + if (isResolverListEmpty(listAdapter)) { + mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter); } else { mMultiProfilePagerAdapter.showListView(listAdapter); } @@ -1640,12 +1640,12 @@ public class ResolverActivity extends Activity implements private void setupViewVisibilities() { ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter(); - if (!shouldShowEmptyState(activeListAdapter)) { + if (!isResolverListEmpty(activeListAdapter)) { addUseDifferentAppLabelIfNecessary(activeListAdapter); } } - private boolean shouldShowEmptyState(ResolverListAdapter listAdapter) { + private boolean isResolverListEmpty(ResolverListAdapter listAdapter) { int count = listAdapter.getUnfilteredCount(); return count == 0 && listAdapter.getPlaceholderCount() == 0; } diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 51dce5547890..3f897a5f26bf 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -120,12 +120,32 @@ public class ResolverListController { boolean shouldGetActivityMetadata, List<Intent> intents, UserHandle userHandle) { + int baseFlags = PackageManager.MATCH_DEFAULT_ONLY + | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) + | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0); + return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags); + } + + /** + * Returns a list of resolved intents which is user state-independent. This means it will + * return the same results regardless of whether the {@code userHandle} user is disabled or not. + */ + public List<ResolverActivity.ResolvedComponentInfo> getUserStateIndependentResolversAsUser( + List<Intent> intents, + UserHandle userHandle) { + int baseFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags); + } + + private List<ResolverActivity.ResolvedComponentInfo> getResolversForIntentAsUserInternal( + List<Intent> intents, + UserHandle userHandle, + int baseFlags) { List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null; for (int i = 0, N = intents.size(); i < N; i++) { final Intent intent = intents.get(i); - int flags = PackageManager.MATCH_DEFAULT_ONLY - | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) - | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0); + int flags = baseFlags; if (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { flags |= PackageManager.MATCH_INSTANT; diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index e427421a5a80..812e2a6c63fd 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -1330,8 +1330,15 @@ public class ChooserActivityTest { createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(workProfileTargets); + when(sOverrides.workResolverListController.getUserStateIndependentResolversAsUser( + Mockito.isA(List.class), + Mockito.isA(UserHandle.class))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); sOverrides.isQuietModeEnabled = true; - setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + // When work profile is disabled, we get 0 results when we query the work profile + // intents. + setupResolverControllers(personalResolvedComponentInfos, + /* workResolvedComponentInfos */ new ArrayList<>()); Intent sendIntent = createSendTextIntent(); sendIntent.setType("TestType"); @@ -1348,7 +1355,7 @@ public class ChooserActivityTest { } @Test - public void testWorkTab_noWorkTargets_emptyStateShown() { + public void testWorkTab_noWorkAppsAvailable_emptyStateShown() { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); @@ -1372,6 +1379,57 @@ public class ChooserActivityTest { .check(matches(isDisplayed())); } + @Test + public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() { + // enable the work tab feature flag + ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTest(3); + List<ResolvedComponentInfo> workResolvedComponentInfos = + createResolvedComponentsForTest(0); + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + sOverrides.isQuietModeEnabled = true; + sOverrides.hasCrossProfileIntents = false; + Intent sendIntent = createSendTextIntent(); + sendIntent.setType("TestType"); + + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); + waitForIdle(); + onView(withId(R.id.contentPanel)) + .perform(swipeUp()); + onView(withText(R.string.resolver_work_tab)).perform(click()); + waitForIdle(); + + onView(withText(R.string.resolver_cant_share_with_work_apps)) + .check(matches(isDisplayed())); + } + + @Test + public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() { + // enable the work tab feature flag + ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTest(3); + List<ResolvedComponentInfo> workResolvedComponentInfos = + createResolvedComponentsForTest(0); + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + sOverrides.isQuietModeEnabled = true; + Intent sendIntent = createSendTextIntent(); + sendIntent.setType("TestType"); + + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test")); + waitForIdle(); + onView(withId(R.id.contentPanel)) + .perform(swipeUp()); + onView(withText(R.string.resolver_work_tab)).perform(click()); + waitForIdle(); + + onView(withText(R.string.resolver_no_work_apps_available_share)) + .check(matches(isDisplayed())); + } + private Intent createSendTextIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 4ec89b7d2817..5a3aff937b79 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -598,7 +598,7 @@ public class ResolverActivityTest { onView(withId(R.id.contentPanel)) .perform(swipeUp()); - onView(withText(R.string.resolver_cant_share_with_work_apps)) + onView(withText(R.string.resolver_cant_access_work_apps)) .check(matches(isDisplayed())); } @@ -612,8 +612,15 @@ public class ResolverActivityTest { createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(workProfileTargets); + when(sOverrides.workResolverListController.getUserStateIndependentResolversAsUser( + Mockito.isA(List.class), + Mockito.isA(UserHandle.class))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); sOverrides.isQuietModeEnabled = true; - setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + // When work profile is disabled, we get 0 results when we query the work profile + // intents. + setupResolverControllers(personalResolvedComponentInfos, + /* workResolvedComponentInfos */ new ArrayList<>()); Intent sendIntent = createSendImageIntent(); sendIntent.setType("TestType"); @@ -629,7 +636,31 @@ public class ResolverActivityTest { } @Test - public void testWorkTab_noWorkTargets_emptyStateShown() { + public void testWorkTab_noWorkAppsAvailable_emptyStateShown() { + // enable the work tab feature flag + ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTest(3); + List<ResolvedComponentInfo> workResolvedComponentInfos = + createResolvedComponentsForTest(0); + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + Intent sendIntent = createSendImageIntent(); + sendIntent.setType("TestType"); + + mActivityRule.launchActivity(sendIntent); + waitForIdle(); + onView(withId(R.id.contentPanel)) + .perform(swipeUp()); + onView(withText(R.string.resolver_work_tab)).perform(click()); + waitForIdle(); + + onView(withText(R.string.resolver_no_work_apps_available_resolve)) + .check(matches(isDisplayed())); + } + + @Test + public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); @@ -640,6 +671,33 @@ public class ResolverActivityTest { setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); Intent sendIntent = createSendImageIntent(); sendIntent.setType("TestType"); + sOverrides.isQuietModeEnabled = true; + sOverrides.hasCrossProfileIntents = false; + + mActivityRule.launchActivity(sendIntent); + waitForIdle(); + onView(withId(R.id.contentPanel)) + .perform(swipeUp()); + onView(withText(R.string.resolver_work_tab)).perform(click()); + waitForIdle(); + + onView(withText(R.string.resolver_cant_access_work_apps)) + .check(matches(isDisplayed())); + } + + @Test + public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() { + // enable the work tab feature flag + ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTest(3); + List<ResolvedComponentInfo> workResolvedComponentInfos = + createResolvedComponentsForTest(0); + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + Intent sendIntent = createSendImageIntent(); + sendIntent.setType("TestType"); + sOverrides.isQuietModeEnabled = true; mActivityRule.launchActivity(sendIntent); waitForIdle(); |