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

Commit a375a998 authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski
Browse files

Add support for efficient move within a document provider.

Currently moving files even within the same document provider causes
reading and writing all of the bytes, which is very inefficient for all
network based solutions, eg. Drive where moving even huge files can be
instant.

This CL adds an optional moveDocument method to the DocumentProvider as
well as DocumentContract so providers can implement an efficient way of
moving things. If not implemented, we would fallback to moving by
streaming.

BUG=None

Change-Id: I74d6754f96cbafa749a04ca9aebf2e0e2b64b515
parent 74fe1816
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26016,6 +26016,7 @@ package android.provider {
    field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
    field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
    field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
    field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
    field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
    field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
    field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
+1 −0
Original line number Diff line number Diff line
@@ -27990,6 +27990,7 @@ package android.provider {
    field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
    field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
    field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
    field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
    field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
    field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
    field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
+48 −3
Original line number Diff line number Diff line
@@ -324,12 +324,21 @@ public final class DocumentsContract {
         * within the same document provider.
         *
         * @see #COLUMN_FLAGS
         * @see DocumentsContract#copyDocument(ContentProviderClient, Uri,
         *      String)
         * @see DocumentsProvider#copyDocument(String, String, String)
         * @see DocumentsContract#copyDocument(ContentProviderClient, Uri, Uri)
         * @see DocumentsProvider#copyDocument(String, String)
         */
        public static final int FLAG_SUPPORTS_COPY = 1 << 7;

        /**
         * Flag indicating that a document can be moved to another location
         * within the same document provider.
         *
         * @see #COLUMN_FLAGS
         * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri)
         * @see DocumentsProvider#moveDocument(String, String)
         */
        public static final int FLAG_SUPPORTS_MOVE = 1 << 8;

        /**
         * Flag indicating that document titles should be hidden when viewing
         * this directory in a larger format grid. For example, a directory
@@ -553,6 +562,8 @@ public final class DocumentsContract {
    public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
    /** {@hide} */
    public static final String METHOD_COPY_DOCUMENT = "android:copyDocument";
    /** {@hide} */
    public static final String METHOD_MOVE_DOCUMENT = "android:moveDocument";

    /** {@hide} */
    public static final String EXTRA_URI = "uri";
@@ -1065,6 +1076,40 @@ public final class DocumentsContract {
        return out.getParcelable(DocumentsContract.EXTRA_URI);
    }

    /**
     * Moves the given document under a new parent.
     *
     * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE}
     * @param targetParentDocumentUri document which will become a new parent of the source
     *         document.
     * @return the moved document, or {@code null} if failed.
     * @hide
     */
    public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
            Uri targetParentDocumentUri) {
        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                sourceDocumentUri.getAuthority());
        try {
            return moveDocument(client, sourceDocumentUri, targetParentDocumentUri);
        } catch (Exception e) {
            Log.w(TAG, "Failed to move document", e);
            return null;
        } finally {
            ContentProviderClient.releaseQuietly(client);
        }
    }

    /** {@hide} */
    public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
            Uri targetParentDocumentUri) throws RemoteException {
        final Bundle in = new Bundle();
        in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);

        final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
        return out.getParcelable(DocumentsContract.EXTRA_URI);
    }

    /**
     * Open the given image for thumbnail purposes, using any embedded EXIF
     * thumbnail if available, and providing orientation hints from the parent
+45 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_MOVE_DOCUMENT;
import static android.provider.DocumentsContract.buildDocumentUri;
import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
import static android.provider.DocumentsContract.buildTreeDocumentUri;
@@ -277,6 +278,24 @@ public abstract class DocumentsProvider extends ContentProvider {
        throw new UnsupportedOperationException("Copy not supported");
    }

    /**
     * Move the requested document or a document tree.
     * <p>
     * Moves a document including all child documents to another location within
     * the same document provider. Upon completion returns the document id of
     * the copied document at the target destination. {@code null} must never
     * be returned.
     *
     * @param sourceDocumentId the document to move.
     * @param targetParentDocumentId the target document to be a new parent of the
     *     source document.
     * @hide
     */
    @SuppressWarnings("unused")
    public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
            throws FileNotFoundException {
        throw new UnsupportedOperationException("Move not supported");
    }
    /**
     * Return all roots currently provided. To display to users, you must define
     * at least one root. You should avoid making network requests to keep this
@@ -725,6 +744,32 @@ public abstract class DocumentsProvider extends ContentProvider {
                    out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
                }

            } else if (METHOD_MOVE_DOCUMENT.equals(method)) {
                final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
                final String targetId = DocumentsContract.getDocumentId(targetUri);

                enforceReadPermissionInner(documentUri, null);
                enforceWritePermissionInner(targetUri, null);

                final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
                final String newDocumentId = moveDocument(documentId, targetId);

                if (newDocumentId != null) {
                    final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
                            newDocumentId);

                    if (!isTreeUri(newDocumentUri)) {
                        final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
                                documentUri);
                        context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
                    }

                    out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
                }

                // Original document no longer exists, clean up any grants
                revokeDocumentPermission(documentId);

            } else {
                throw new UnsupportedOperationException("Method not supported " + method);
            }