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

Commit c4b803a1 authored by Kelvin Kwan's avatar Kelvin Kwan
Browse files

Enable Directory search across profiles

Bug: 148270755
Test: manual
Test: atest DocumentsUIGoogleTests
Change-Id: Ia3821709a48f4595109daa3ce5d911f828cf4d25
parent dbf8a3f3
Loading
Loading
Loading
Loading
+52 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -31,6 +32,7 @@ import android.os.RemoteException;
import android.provider.DocumentsContract.Document;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.loader.content.AsyncTaskLoader;

import com.android.documentsui.archives.ArchivesProvider;
@@ -42,9 +44,12 @@ import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.MimeTypes;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.documentsui.base.UserId;
import com.android.documentsui.roots.RootCursorWrapper;
import com.android.documentsui.sorting.SortModel;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
@@ -57,6 +62,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {

    private final LockingContentObserver mObserver;
    private final RootInfo mRoot;
    private final State mState;
    private final Uri mUri;
    private final SortModel mModel;
    private final Lookup<String, String> mFileTypeLookup;
@@ -81,6 +87,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {

        super(context);
        mFeatures = features;
        mState = state;
        mRoot = state.stack.getRoot();
        mUri = uri;
        mModel = state.sortModel;
@@ -124,8 +131,16 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
            final Bundle queryArgs = new Bundle();
            mModel.addQuerySortArgs(queryArgs);

            final List<UserId> userIds = new ArrayList<>();
            if (mSearchMode) {
                queryArgs.putAll(mQueryArgs);
                if (mState.canShareAcrossProfile && mRoot.supportsCrossProfile()) {
                    userIds.addAll(
                            DocumentsApplication.getUserIdManager(getContext()).getUserIds());
                }
            }
            if (userIds.isEmpty()) {
                userIds.add(mDoc.userId);
            }

            if (mFeatures.isContentPagingEnabled()) {
@@ -134,17 +149,13 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
                DebugFlags.addForcedPagingArgs(queryArgs);
            }

            cursor = client.query(mUri, null, queryArgs, mSignal);
            cursor = queryOnUsers(userIds, authority, queryArgs);

            if (cursor == null) {
                throw new RemoteException("Provider returned null");
            }

            cursor.registerContentObserver(mObserver);

            cursor = new RootCursorWrapper(mDoc.userId, mUri.getAuthority(), mRoot.rootId, cursor,
                    -1);

            if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) {
                // There is no findDocumentPath API. Enable filtering on folders in search mode.
                cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
@@ -176,6 +187,38 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
        return result;
    }

    @Nullable
    private Cursor queryOnUsers(List<UserId> userIds, String authority, Bundle queryArgs)
            throws RemoteException {
        final List<Cursor> cursors = new ArrayList<>(userIds.size());
        for (UserId userId : userIds) {
            try (ContentProviderClient userClient =
                         DocumentsApplication.acquireUnstableProviderOrThrow(
                                 userId.getContentResolver(getContext()), authority)) {
                Cursor c = userClient.query(mUri, /* projection= */null, queryArgs, mSignal);
                if (c != null) {
                    cursors.add(new RootCursorWrapper(userId, mUri.getAuthority(), mRoot.rootId,
                            c, /* maxCount= */-1));
                }
            } catch (RemoteException e) {
                Log.d(TAG, "Failed to query for user " + userId, e);
                // Searching on other profile may not succeed because profile may be in quiet mode.
                if (UserId.CURRENT_USER.equals(userId)) {
                    throw e;
                }
            }
        }
        int size = cursors.size();
        switch (size) {
            case 0:
                return null;
            case 1:
                return cursors.get(0);
            default:
                return new MergeCursor(cursors.toArray(new Cursor[size]));
        }
    }

    @Override
    public void cancelLoadInBackground() {
        super.cancelLoadInBackground();
@@ -232,9 +275,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
        // Ensure the loader is stopped
        onStopLoading();

        if (mResult.cursor != null && mObserver != null) {
            mResult.cursor.unregisterContentObserver(mObserver);
        }

        FileUtils.closeQuietly(mResult);
        mResult = null;

        mDoc.userId.getContentResolver(getContext()).unregisterContentObserver(mObserver);
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static com.android.documentsui.base.SharedMinimal.DEBUG;
import static com.android.documentsui.base.SharedMinimal.TAG;

import android.database.AbstractCursor;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.DocumentsContract.Document;
@@ -136,4 +137,14 @@ public class FilteringCursorWrapper extends AbstractCursor {
    public boolean isNull(int column) {
        return mCursor.isNull(column);
    }

    @Override
    public void registerContentObserver(ContentObserver observer) {
        mCursor.registerContentObserver(observer);
    }

    @Override
    public void unregisterContentObserver(ContentObserver observer) {
        mCursor.unregisterContentObserver(observer);
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -84,6 +84,11 @@ public class State implements android.os.Parcelable {

    public boolean openableOnly;

    /**
     * Represents whether the intent is a cross-profile intent
     */
    public boolean canShareAcrossProfile = false;

    /**
     * This is basically a sub-type for the copy operation. It can be either COPY,
     * COMPRESS, EXTRACT or MOVE.
+10 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ 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;
@@ -206,6 +208,14 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
            final Intent moreApps = new Intent(intent);
            moreApps.setComponent(null);
            moreApps.setPackage(null);
            for (ResolveInfo info : getPackageManager().queryIntentActivities(moreApps,
                    PackageManager.MATCH_DEFAULT_ONLY)) {
                if (RootsFragment.PROFILE_TARGET_ACTIVITY.equals(
                        info.activityInfo.targetActivity)) {
                    mState.canShareAcrossProfile = true;
                    break;
                }
            }
            RootsFragment.show(getSupportFragmentManager(), moreApps);
        } else if (mState.action == ACTION_OPEN ||
                   mState.action == ACTION_CREATE ||
+3 −1
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ public class RootsFragment extends Fragment {

    private static final String TAG = "RootsFragment";
    private static final String EXTRA_INCLUDE_APPS = "includeApps";
    private static final String PROFILE_TARGET_ACTIVITY =
    public static final String PROFILE_TARGET_ACTIVITY =
            "com.android.internal.app.IntentForwarderActivity";
    private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500;

@@ -384,6 +384,8 @@ public class RootsFragment extends Fragment {

                // for change personal profile root.
                if (PROFILE_TARGET_ACTIVITY.equals(info.activityInfo.targetActivity)) {
                    // TODO: only set in current user
                    getBaseActivity().getDisplayState().canShareAcrossProfile = true;
                    profileItem = new ProfileItem(info, info.loadLabel(pm).toString(),
                            mActionHandler);
                } else {
Loading