Merge "Add support for efficient move within a document provider."
diff --git a/api/current.txt b/api/current.txt
index dd7e326..2737792 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26016,6 +26016,7 @@
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
diff --git a/api/system-current.txt b/api/system-current.txt
index bbfe9bb..9df21c2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -27990,6 +27990,7 @@
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
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 50f3038..9eded31 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -324,13 +324,22 @@
* 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
* containing only images may want the image thumbnails to speak for
@@ -553,6 +562,8 @@
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";
@@ -1066,6 +1077,40 @@
}
/**
+ * 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
* image.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 87c8fca..ef188a2 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -20,6 +20,7 @@
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;
@@ -278,6 +279,24 @@
}
/**
+ * 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
* request fast.
@@ -725,6 +744,32 @@
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);
}