Merge "DocsUI to support other intent action for file picking" into rvc-dev am: 8242882a75

Change-Id: I3d4432463d152d47d07545848499c959695eb5b5
diff --git a/src/com/android/documentsui/dirlist/Message.java b/src/com/android/documentsui/dirlist/Message.java
index 0965ae4..4fc7c49 100644
--- a/src/com/android/documentsui/dirlist/Message.java
+++ b/src/com/android/documentsui/dirlist/Message.java
@@ -143,6 +143,7 @@
                 update(null, mEnv.getModel().info, null,
                         mEnv.getContext().getDrawable(R.drawable.ic_dialog_info));
             } else if (mEnv.getDisplayState().action == State.ACTION_OPEN_TREE
+                    && mEnv.getDisplayState().stack.peek() != null
                     && mEnv.getDisplayState().stack.peek().isBlockedFromTree()
                     && mEnv.getDisplayState().restrictScopeStorage) {
                 updateBlockFromTreeMessage();
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index f1a58eb..084835b 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -171,7 +171,8 @@
                         mDrawer,
                         mInjector.searchManager::onSearchBarClicked);
 
-        RootsFragment.show(getSupportFragmentManager(), null);
+        RootsFragment.show(getSupportFragmentManager(), /* includeApps= */ false,
+                /* intent= */ null);
 
         final Intent intent = getIntent();
 
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index add9643..9bc337f 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -23,8 +23,6 @@
 import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION;
 
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.net.Uri;
@@ -212,23 +210,23 @@
             saveContainer.setBackgroundColor(Color.TRANSPARENT);
         }
 
-        if (mState.action == ACTION_GET_CONTENT) {
-            final Intent moreApps = new Intent(intent);
-            moreApps.setComponent(null);
-            moreApps.setPackage(null);
-            for (ResolveInfo info : getPackageManager().queryIntentActivities(moreApps,
-                    PackageManager.MATCH_DEFAULT_ONLY)) {
-                if (CrossProfileUtils.isCrossProfileIntentForwarderActivity(info)) {
-                    mState.canShareAcrossProfile = true;
-                    break;
-                }
-            }
-            RootsFragment.show(getSupportFragmentManager(), moreApps);
-        } else if (mState.action == ACTION_OPEN ||
-                   mState.action == ACTION_CREATE ||
-                   mState.action == ACTION_OPEN_TREE ||
-                   mState.action == ACTION_PICK_COPY_DESTINATION) {
-            RootsFragment.show(getSupportFragmentManager(), (Intent) null);
+        final Intent moreApps = new Intent(intent);
+        moreApps.setComponent(null);
+        moreApps.setPackage(null);
+        if (mState.supportsCrossProfile()
+                && CrossProfileUtils.getCrossProfileResolveInfo(
+                        getPackageManager(), moreApps) != null) {
+            mState.canShareAcrossProfile = true;
+        }
+
+        if (mState.action == ACTION_GET_CONTENT
+                || mState.action == ACTION_OPEN
+                || mState.action == ACTION_CREATE
+                || mState.action == ACTION_OPEN_TREE
+                || mState.action == ACTION_PICK_COPY_DESTINATION) {
+            RootsFragment.show(getSupportFragmentManager(),
+                    /* includeApps= */ mState.action == ACTION_GET_CONTENT,
+                    /* intent= */ moreApps);
         }
     }
 
@@ -266,8 +264,9 @@
             state.copyOperationSubType = intent.getIntExtra(
                     FileOperationService.EXTRA_OPERATION_TYPE,
                     FileOperationService.OPERATION_COPY);
-        } else if (Features.CROSS_PROFILE_TABS && VersionUtils.isAtLeastR()
-                && state.action == ACTION_GET_CONTENT) {
+        } else if (Features.CROSS_PROFILE_TABS && VersionUtils.isAtLeastR()) {
+            // We show tabs on PickActivity except copying/moving, which does not support
+            // cross-profile action.
             state.supportsCrossProfile = true;
         }
     }
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index e1d5f18..79c9f1c 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -93,6 +93,7 @@
 
     private static final String TAG = "RootsFragment";
     private static final String EXTRA_INCLUDE_APPS = "includeApps";
+    private static final String EXTRA_INCLUDE_APPS_INTENT = "includeAppsIntent";
     private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500;
 
     private final OnItemClickListener mItemListener = new OnItemClickListener() {
@@ -126,9 +127,18 @@
 
     private List<Item> mApplicationItemList;
 
-    public static RootsFragment show(FragmentManager fm, Intent includeApps) {
+    /**
+     * Shows the {@link RootsFragment}.
+     * @param fm the FragmentManager for interacting with fragments associated with this
+     *           fragment's activity
+     * @param includeApps if {@code true}, query the intent from the system and include apps in
+     *                    the {@RootsFragment}.
+     * @param intent the intent to query for package manager
+     */
+    public static RootsFragment show(FragmentManager fm, boolean includeApps, Intent intent) {
         final Bundle args = new Bundle();
-        args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
+        args.putBoolean(EXTRA_INCLUDE_APPS, includeApps);
+        args.putParcelable(EXTRA_INCLUDE_APPS_INTENT, intent);
 
         final RootsFragment fragment = new RootsFragment();
         fragment.setArguments(args);
@@ -240,7 +250,9 @@
                     return;
                 }
 
-                Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
+                boolean shouldIncludeHandlerApp = getArguments().getBoolean(EXTRA_INCLUDE_APPS,
+                        /* defaultValue= */ false);
+                Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS_INTENT);
 
                 final Intent intent = activity.getIntent();
                 final boolean excludeSelf =
@@ -248,12 +260,37 @@
                 final String excludePackage = excludeSelf ? activity.getCallingPackage() : null;
                 final boolean maybeShowBadge =
                         getBaseActivity().getDisplayState().supportsCrossProfile();
-                List<Item> sortedItems = sortLoadResult(roots, excludePackage, handlerAppIntent,
+
+                // For action which supports cross profile, update the policy value in state if
+                // necessary.
+                ResolveInfo crossProfileResolveInfo = null;
+                if (state.supportsCrossProfile() && handlerAppIntent != null) {
+                    crossProfileResolveInfo = CrossProfileUtils.getCrossProfileResolveInfo(
+                            getContext().getPackageManager(), handlerAppIntent);
+                    updateCrossProfileStateAndMaybeRefresh(
+                            /* canShareAcrossProfile= */ crossProfileResolveInfo != null);
+                }
+
+                List<Item> sortedItems = sortLoadResult(
+                        state,
+                        roots,
+                        excludePackage,
+                        shouldIncludeHandlerApp ? handlerAppIntent : null,
                         DocumentsApplication.getProvidersCache(getContext()),
                         getBaseActivity().getSelectedUser(),
                         DocumentsApplication.getUserIdManager(getContext()).getUserIds(),
                         maybeShowBadge);
 
+                // This will be removed when feature flag is removed.
+                if (crossProfileResolveInfo != null && !Features.CROSS_PROFILE_TABS) {
+                    // Add profile item if we don't support cross-profile tab.
+                    sortedItems.add(new SpacerItem());
+                    sortedItems.add(new ProfileItem(crossProfileResolveInfo,
+                            crossProfileResolveInfo.loadLabel(
+                                    getContext().getPackageManager()).toString(),
+                            mActionHandler));
+                }
+
                 // Disable drawer if only one root
                 activity.setRootsDrawerLocked(sortedItems.size() <= 1);
 
@@ -286,6 +323,20 @@
     }
 
     /**
+     * Updates the state values of whether we can share across profiles, if necessary. Also reload
+     * documents stack if the selected user is not the current user.
+     */
+    private void updateCrossProfileStateAndMaybeRefresh(boolean canShareAcrossProfile) {
+        final State state = getBaseActivity().getDisplayState();
+        if (state.canShareAcrossProfile != canShareAcrossProfile) {
+            state.canShareAcrossProfile = canShareAcrossProfile;
+            if (!UserId.CURRENT_USER.equals(getBaseActivity().getSelectedUser())) {
+                mActionHandler.loadDocumentsForCurrentStack();
+            }
+        }
+    }
+
+    /**
      * If the package name of other providers or apps capable of handling the original intent
      * include the preferred root source, it will have higher order than others.
      * @param excludePackage Exclude activities from this given package
@@ -294,6 +345,7 @@
      */
     @VisibleForTesting
     List<Item> sortLoadResult(
+            State state,
             Collection<RootInfo> roots,
             @Nullable String excludePackage,
             @Nullable Intent handlerAppIntent,
@@ -344,24 +396,50 @@
         if (VERBOSE) Log.v(TAG, "Adding storage roots: " + storageProviders);
         result.addAll(storageProviders);
 
-        // Include apps that can handle this intent too.
+
+        final List<Item> rootList = new ArrayList<>();
+        final List<Item> rootListOtherUser = new ArrayList<>();
+        mApplicationItemList = new ArrayList<>();
         if (handlerAppIntent != null) {
-            includeHandlerApps(handlerAppIntent, excludePackage, result, otherProviders, userIds,
-                    maybeShowBadge);
+            includeHandlerApps(state, handlerAppIntent, excludePackage, rootList, rootListOtherUser,
+                    otherProviders, userIds, maybeShowBadge);
         } else {
             // Only add providers
             Collections.sort(otherProviders, comp);
-            if (!result.isEmpty() && !otherProviders.isEmpty()) {
-                result.add(new SpacerItem());
-            }
-            if (VERBOSE) Log.v(TAG, "Adding plain roots: " + otherProviders);
-            result.addAll(otherProviders);
-
-            mApplicationItemList = new ArrayList<>();
-            for (Item item : otherProviders) {
+            for (RootItem item : otherProviders) {
+                if (UserId.CURRENT_USER.equals(item.userId)) {
+                    rootList.add(item);
+                } else {
+                    rootListOtherUser.add(item);
+                }
                 mApplicationItemList.add(item);
             }
         }
+
+
+        if (state.supportsCrossProfile() && state.canShareAcrossProfile
+                && !rootList.isEmpty() && !rootListOtherUser.isEmpty()) {
+            // Identify personal and work root list.
+            final List<Item> personalRootList;
+            final List<Item> workRootList;
+            if (UserId.CURRENT_USER.isSystem()) {
+                personalRootList = rootList;
+                workRootList = rootListOtherUser;
+            } else {
+                personalRootList = rootListOtherUser;
+                workRootList = rootList;
+            }
+
+            // Add header and list to the result
+            final List<Item> resultRootList = new ArrayList<>();
+            resultRootList.add(new HeaderItem(getString(R.string.personal_tab)));
+            resultRootList.addAll(personalRootList);
+            resultRootList.add(new HeaderItem(getString(R.string.work_tab)));
+            resultRootList.addAll(workRootList);
+            addListToResult(result, resultRootList);
+        } else {
+            addListToResult(result, rootList);
+        }
         return result;
     }
 
@@ -369,18 +447,15 @@
      * Adds apps capable of handling the original intent will be included in list of roots. If
      * the providers and apps are the same package name, combine them as RootAndAppItems.
      */
-    private void includeHandlerApps(
-            Intent handlerAppIntent, @Nullable String excludePackage, List<Item> result,
-            List<RootItem> otherProviders, List<UserId> userIds, boolean maybeShowBadge) {
+    private void includeHandlerApps(State state,
+            Intent handlerAppIntent, @Nullable String excludePackage, List<Item> rootList,
+            List<Item> rootListOtherUser, List<RootItem> otherProviders, List<UserId> userIds,
+            boolean maybeShowBadge) {
         if (VERBOSE) Log.v(TAG, "Adding handler apps for intent: " + handlerAppIntent);
 
         Context context = getContext();
-        final List<Item> rootList = new ArrayList<>();
-        final List<Item> rootListOtherUser = new ArrayList<>();
-        final List<Item> resultRootList = new ArrayList<>();
         final Map<UserPackage, ResolveInfo> appsMapping = new HashMap<>();
         final Map<UserPackage, Item> appItems = new HashMap<>();
-        ProfileItem profileItem = null;
 
         final String myPackageName = context.getPackageName();
         for (UserId userId : userIds) {
@@ -403,13 +478,7 @@
                     UserPackage userPackage = new UserPackage(userId, packageName);
                     appsMapping.put(userPackage, info);
 
-                    // for change personal profile root.
-                    if (CrossProfileUtils.isCrossProfileIntentForwarderActivity(info)) {
-                        if (UserId.CURRENT_USER.equals(userId)) {
-                            profileItem = new ProfileItem(info, info.loadLabel(pm).toString(),
-                                    mActionHandler);
-                        }
-                    } else {
+                    if (!CrossProfileUtils.isCrossProfileIntentForwarderActivity(info)) {
                         final Item item = new AppItem(info, info.loadLabel(pm).toString(), userId,
                                 mActionHandler);
                         appItems.put(userPackage, item);
@@ -419,14 +488,6 @@
             }
         }
 
-        boolean canShareAcrossProfile = profileItem != null;
-        if (getBaseActivity().getDisplayState().canShareAcrossProfile != canShareAcrossProfile) {
-            getBaseActivity().getDisplayState().canShareAcrossProfile = canShareAcrossProfile;
-            if (!UserId.CURRENT_USER.equals(getBaseActivity().getSelectedUser())) {
-                mActionHandler.loadDocumentsForCurrentStack();
-            }
-        }
-
         // If there are some providers and apps has the same package name, combine them as one item.
         for (RootItem rootItem : otherProviders) {
             final UserPackage userPackage = new UserPackage(rootItem.userId,
@@ -462,55 +523,14 @@
         final String preferredRootPackage = getResources().getString(
                 R.string.preferred_root_package, "");
         final ItemComparator comp = new ItemComparator(preferredRootPackage);
+        Collections.sort(rootList, comp);
+        Collections.sort(rootListOtherUser, comp);
 
-        if (canShareAcrossProfile && Features.CROSS_PROFILE_TABS) {
-            // Combine lists only if we enabled profile tab feature.
-            if (!rootList.isEmpty() && !rootListOtherUser.isEmpty()) {
-                // Identify personal and work root list.
-                final List<Item> personalRootList;
-                final List<Item> workRootList;
-                if (UserId.CURRENT_USER.isSystem()) {
-                    personalRootList = rootList;
-                    workRootList = rootListOtherUser;
-                } else {
-                    personalRootList = rootListOtherUser;
-                    workRootList = rootList;
-                }
-                Collections.sort(personalRootList, comp);
-                Collections.sort(workRootList, comp);
-
-                // Make sure mApplicationItemList has items from both profiles.
-                final List<Item> mergeRootList =
-                        new ArrayList<>(rootList.size() + rootListOtherUser.size());
-                mergeRootList.addAll(rootList);
-                mergeRootList.addAll(rootListOtherUser);
-                mApplicationItemList = mergeRootList;
-
-                // Add header and list to the result
-                resultRootList.add(new HeaderItem(getString(R.string.personal_tab)));
-                resultRootList.addAll(personalRootList);
-                resultRootList.add(new HeaderItem(getString(R.string.work_tab)));
-                resultRootList.addAll(workRootList);
-            } else {
-                // There is no more than 1 user, we can add all lists to result without inserting
-                // personal or work header.
-                resultRootList.addAll(rootList);
-                resultRootList.addAll(rootListOtherUser);
-                Collections.sort(resultRootList, comp);
-                mApplicationItemList = resultRootList;
-            }
+        if (state.supportsCrossProfile() && state.canShareAcrossProfile) {
+            mApplicationItemList.addAll(rootList);
+            mApplicationItemList.addAll(rootListOtherUser);
         } else {
-            resultRootList.addAll(rootList);
-            Collections.sort(resultRootList, comp);
-            mApplicationItemList = resultRootList;
-        }
-        addListToResult(result, resultRootList);
-
-        // This will be removed when feature flag is removed.
-        if (canShareAcrossProfile && !Features.CROSS_PROFILE_TABS) {
-            // Add profile item if we don't support cross-profile tab.
-            result.add(new SpacerItem());
-            result.add(profileItem);
+            mApplicationItemList.addAll(rootList);
         }
     }
 
diff --git a/src/com/android/documentsui/util/CrossProfileUtils.java b/src/com/android/documentsui/util/CrossProfileUtils.java
index 2e32b42..e60ffdd 100644
--- a/src/com/android/documentsui/util/CrossProfileUtils.java
+++ b/src/com/android/documentsui/util/CrossProfileUtils.java
@@ -16,8 +16,12 @@
 
 package com.android.documentsui.util;
 
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 
+import androidx.annotation.Nullable;
+
 /**
  * A utility class for cross-profile usage.
  */
@@ -40,4 +44,20 @@
         }
         return false;
     }
+
+    /**
+     * Returns the {@ResolveInfo} if this intent is a cross-profile intent or {@code null}
+     * otherwise.
+     */
+    @Nullable
+    public static ResolveInfo getCrossProfileResolveInfo(PackageManager packageManager,
+            Intent intent) {
+        for (ResolveInfo info : packageManager.queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY)) {
+            if (isCrossProfileIntentForwarderActivity(info)) {
+                return info;
+            }
+        }
+        return null;
+    }
 }
diff --git a/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java b/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java
index 8a342ba..568740b 100644
--- a/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java
+++ b/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java
@@ -26,6 +26,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.State;
 import com.android.documentsui.base.UserId;
 import com.android.documentsui.testing.TestProvidersAccess;
 import com.android.documentsui.testing.TestResolveInfo;
@@ -68,7 +69,9 @@
 
     @Test
     public void testSortLoadResult_WithCorrectOrder() {
-        List<Item> items = mRootsFragment.sortLoadResult(createFakeRootInfoList(),
+        List<Item> items = mRootsFragment.sortLoadResult(
+                new State(),
+                createFakeRootInfoList(),
                 null /* excludePackage */, null /* handlerAppIntent */, new TestProvidersAccess(),
                 UserId.DEFAULT_USER,
                 Collections.singletonList(UserId.DEFAULT_USER),