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

Commit 2e81db6f authored by Garfield Tan's avatar Garfield Tan
Browse files

Fix crash when dragging documents from Recents.

Also not allow user to move from Recents (DnD or Ctrl+X).

Bug: 37712649
Change-Id: I5d63b6359ca478e77479872832f8f759d17c92e6
parent a14c1733
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -500,7 +500,8 @@ public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
            if (mState.stack.isRecents()) {

                if (DEBUG) Log.d(TAG, "Creating new loader recents.");
                return new RecentsLoader(context, mProviders, mState, mInjector.features);
                return new RecentsLoader(
                        context, mProviders, mState, mInjector.features, mExecutors);

            } else {

+15 −11
Original line number Diff line number Diff line
@@ -67,19 +67,19 @@ public interface DragAndDropManager {
     * @param v the view which
     *          {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} will be
     *          called.
     * @param parent {@link DocumentInfo} of the container of srcs
     * @param srcs documents that are dragged
     * @param root the root in which documents being dragged are
     * @param invalidDest destinations that don't accept this drag and drop
     * @param iconHelper used to load document icons
     * @param parent {@link DocumentInfo} of the container of srcs
     */
    void startDrag(
            View v,
            DocumentInfo parent,
            List<DocumentInfo> srcs,
            RootInfo root,
            List<Uri> invalidDest,
            IconHelper iconHelper);
            IconHelper iconHelper,
            @Nullable DocumentInfo parent);

    /**
     * Checks whether the document can be spring opened.
@@ -167,10 +167,6 @@ public interface DragAndDropManager {
        // type of file operations.
        private boolean mIsCtrlPressed;

        // Boolean flag for current drag and drop operation. Returns true if the files can only
        // be copied (ie. Read-Only files)
        private boolean mMustBeCopied;

        // Drag events info. These are used to derive state and update drag shadow when user changes
        // Ctrl key state.
        private View mView;
@@ -179,6 +175,10 @@ public interface DragAndDropManager {
        private RootInfo mDestRoot;
        private DocumentInfo mDestDoc;

        // Boolean flag for current drag and drop operation. Returns true if the files can only
        // be copied (ie. files that don't support delete or remove).
        private boolean mMustBeCopied;

        private RuntimeDragAndDropManager(Context context, DocumentClipper clipper) {
            this(
                    context.getApplicationContext(),
@@ -223,11 +223,11 @@ public interface DragAndDropManager {
        @Override
        public void startDrag(
                View v,
                DocumentInfo parent,
                List<DocumentInfo> srcs,
                RootInfo root,
                List<Uri> invalidDest,
                IconHelper iconHelper) {
                IconHelper iconHelper,
                @Nullable DocumentInfo parent) {

            mView = v;
            mInvalidDest = invalidDest;
@@ -235,11 +235,15 @@ public interface DragAndDropManager {
            List<Uri> uris = new ArrayList<>(srcs.size());
            for (DocumentInfo doc : srcs) {
                uris.add(doc.derivedUri);
                if (!doc.isRemoveSupported() && !doc.isDeleteSupported()) {
                if (!doc.isRemoveSupported()
                        && !doc.isDeleteSupported()
                        && !doc.isMoveSupported()) {
                    mMustBeCopied = true;
                }
            }
            mClipData = mClipper.getClipDataForDocuments(
            mClipData = (parent == null)
                    ? mClipper.getClipDataForDocuments(uris, FileOperationService.OPERATION_UNKNOWN)
                    : mClipper.getClipDataForDocuments(
                            uris, FileOperationService.OPERATION_UNKNOWN, parent);
            mClipData.getDescription().getExtras()
                    .putString(SRC_ROOT_KEY, root.getUri().toString());
+31 −5
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.AsyncTaskLoader;
import android.content.ContentProviderClient;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
@@ -35,6 +36,7 @@ import android.util.Log;

import com.android.documentsui.base.Features;
import com.android.documentsui.base.FilteringCursorWrapper;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.documentsui.roots.ProvidersAccess;
@@ -54,6 +56,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

@@ -84,6 +87,7 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
    private final ProvidersAccess mProviders;
    private final State mState;
    private final Features mFeatures;
    private final Lookup<String, Executor> mExecutors;

    @GuardedBy("mTasks")
    /** A authority -> RecentsTask map */
@@ -94,11 +98,14 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {

    private DirectoryResult mResult;

    public RecentsLoader(Context context, ProvidersAccess providers, State state, Features features) {
    public RecentsLoader(Context context, ProvidersAccess providers, State state, Features features,
            Lookup<String, Executor> executors) {

        super(context);
        mProviders = providers;
        mState = state;
        mFeatures = features;
        mExecutors = executors;

        // Keep clients around on high-RAM devices, since we'd be spinning them
        // up moments later to fetch thumbnails anyway.
@@ -127,7 +134,7 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {

            mFirstPassLatch = new CountDownLatch(mTasks.size());
            for (RecentsTask task : mTasks.values()) {
                ProviderExecutor.forAuthority(task.authority).execute(task);
                mExecutors.lookup(task.authority).execute(task);
            }

            try {
@@ -196,7 +203,8 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
            merged = new MatrixCursor(new String[0]);
        }

        final Cursor sorted = mState.sortModel.sortCursor(merged);
        final Cursor notMovableMasked = new NotMovableMaskCursor(merged);
        final Cursor sorted = mState.sortModel.sortCursor(notMovableMasked);

        // Tell the UI if this is an in-progress result. When loading is complete, another update is
        // sent with EXTRA_LOADING set to false.
@@ -292,7 +300,7 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
    // TODO: create better transfer of ownership around cursor to ensure its
    // closed in all edge cases.

    public class RecentsTask extends AbstractFuture<Cursor[]> implements Runnable, Closeable {
    private class RecentsTask extends AbstractFuture<Cursor[]> implements Runnable, Closeable {
        public final String authority;
        public final List<String> rootIds;

@@ -321,7 +329,7 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
            }
        }

        public synchronized void runInternal() {
        private synchronized void runInternal() {
            if (mIsClosed) {
                return;
            }
@@ -379,4 +387,22 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
            mIsClosed = true;
        }
    }

    private static class NotMovableMaskCursor extends CursorWrapper {
        private static final int NOT_MOVABLE_MASK =
                ~(Document.FLAG_SUPPORTS_DELETE
                        | Document.FLAG_SUPPORTS_REMOVE
                        | Document.FLAG_SUPPORTS_MOVE);

        private NotMovableMaskCursor(Cursor cursor) {
            super(cursor);
        }

        @Override
        public int getInt(int index) {
            final int flagIndex = getWrappedCursor().getColumnIndex(Document.COLUMN_FLAGS);
            final int value = super.getInt(index);
            return (index == flagIndex) ? (value & NOT_MOVABLE_MASK) : value;
        }
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@ import java.util.function.Predicate;
 */
public final class DocumentFilters {

    private static int MOVABLE_MASK = Document.FLAG_SUPPORTS_REMOVE
            | Document.FLAG_SUPPORTS_DELETE
            | Document.FLAG_SUPPORTS_MOVE;

    public static final Predicate<Cursor> ANY = (Cursor c) -> { return true; };
    public static final Predicate<Cursor> VIRTUAL  = DocumentFilters::isVirtual;
    public static final Predicate<Cursor> NOT_MOVABLE = DocumentFilters::isNotMovable;
@@ -77,7 +81,6 @@ public final class DocumentFilters {
     */
    private static final boolean isNotMovable(Cursor c) {
        int flags = getCursorInt(c, Document.COLUMN_FLAGS);
        return (flags & Document.FLAG_SUPPORTS_REMOVE) == 0
                && (flags & Document.FLAG_SUPPORTS_DELETE) == 0;
        return (flags & MOVABLE_MASK) == 0;
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -247,6 +247,10 @@ public class DocumentInfo implements Durable, Parcelable {
        return (flags & Document.FLAG_SUPPORTS_REMOVE) != 0;
    }

    public boolean isMoveSupported() {
        return (flags & Document.FLAG_SUPPORTS_MOVE) != 0;
    }

    public boolean isRenameSupported() {
        return (flags & Document.FLAG_SUPPORTS_RENAME) != 0;
    }
Loading