Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe610292 authored by Kelvin Kwan's avatar Kelvin Kwan Committed by Automerger Merge Worker
Browse files

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

Change-Id: I64bd521f056aea6134012d9c8980c90b9478e694
parents 10e6c71e 8242882a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ abstract class Message {
                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();
+2 −1
Original line number Diff line number Diff line
@@ -171,7 +171,8 @@ public class FilesActivity extends BaseActivity implements AbstractActionHandler
                        mDrawer,
                        mInjector.searchManager::onSearchBarClicked);

        RootsFragment.show(getSupportFragmentManager(), null);
        RootsFragment.show(getSupportFragmentManager(), /* includeApps= */ false,
                /* intent= */ null);

        final Intent intent = getIntent();

+20 −21
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@ import static com.android.documentsui.base.State.ACTION_OPEN_TREE;
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 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
            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)) {
        if (mState.supportsCrossProfile()
                && CrossProfileUtils.getCrossProfileResolveInfo(
                        getPackageManager(), moreApps) != null) {
            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);

        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 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
            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;
        }
    }
+103 −83
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ public class RootsFragment extends Fragment {

    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 @@ public class RootsFragment extends Fragment {

    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 @@ public class RootsFragment extends Fragment {
                    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 @@ public class RootsFragment extends Fragment {
                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);

@@ -285,6 +322,20 @@ public class RootsFragment extends Fragment {
        };
    }

    /**
     * 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.
@@ -294,6 +345,7 @@ public class RootsFragment extends Fragment {
     */
    @VisibleForTesting
    List<Item> sortLoadResult(
            State state,
            Collection<RootInfo> roots,
            @Nullable String excludePackage,
            @Nullable Intent handlerAppIntent,
@@ -344,24 +396,50 @@ public class RootsFragment extends Fragment {
        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());
            for (RootItem item : otherProviders) {
                if (UserId.CURRENT_USER.equals(item.userId)) {
                    rootList.add(item);
                } else {
                    rootListOtherUser.add(item);
                }
            if (VERBOSE) Log.v(TAG, "Adding plain roots: " + otherProviders);
            result.addAll(otherProviders);

            mApplicationItemList = new ArrayList<>();
            for (Item item : otherProviders) {
                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 @@ public class RootsFragment extends Fragment {
     * 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 @@ public class RootsFragment extends Fragment {
                    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 @@ public class RootsFragment extends Fragment {
            }
        }

        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 @@ public class RootsFragment extends Fragment {
        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;
        if (state.supportsCrossProfile() && state.canShareAcrossProfile) {
            mApplicationItemList.addAll(rootList);
            mApplicationItemList.addAll(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;
            }
        } 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);
        }
    }

+20 −0
Original line number Diff line number Diff line
@@ -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 @@ public class CrossProfileUtils {
        }
        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;
    }
}
Loading