summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-08-31 15:02:20 -0700
committerJeff Sharkey <jsharkey@android.com>2013-08-31 16:58:28 -0700
commitae9b51bfa313c51a31af30875a71255d7b6d2e61 (patch)
tree97179597056ef53aafbf952b22bf332954de0649 /core
parentaeb16e2435f9975b9fa1fc4b747796647a21292e (diff)
downloadframeworks_base-ae9b51bfa313c51a31af30875a71255d7b6d2e61.zip
frameworks_base-ae9b51bfa313c51a31af30875a71255d7b6d2e61.tar.gz
frameworks_base-ae9b51bfa313c51a31af30875a71255d7b6d2e61.tar.bz2
Refactoring of DocumentsContract.
Combines related columns and constants onto the same class so they are easier to discover. Move back to surfacing roots with columns so they are consistent with documents. Advanced roots are represented with a flag instead of distinct types. Flags to indicate supporting of well-known media types, instead of arbitrary an MIME filter. Reintroduce well-formed rootId to support recents. Always use the expanded version of "documents" in constants, methods, and argument names. Refactor DocumentProvider method names to clearly distinguish if a single item or multiple could be returned, and of which type. Add documentation to clearly define which methods have already been overridden. Bug: 10567506, 10567557 Change-Id: I981f26ab82f2b520a19aa1ce66f659de50d7fac0
Diffstat (limited to 'core')
-rw-r--r--core/java/android/provider/DocumentsContract.java751
-rw-r--r--core/java/android/provider/DocumentsProvider.java229
2 files changed, 534 insertions, 446 deletions
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ebb7eb8..b97b7c0 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -19,7 +19,6 @@ package android.provider;
import static android.net.TrafficStats.KB_IN_BYTES;
import static libcore.io.OsConstants.SEEK_SET;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -30,16 +29,13 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Parcel;
+import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
-import android.os.Parcelable;
import android.util.Log;
-import com.android.internal.util.Preconditions;
import com.google.android.collect.Lists;
import libcore.io.ErrnoException;
@@ -62,9 +58,12 @@ import java.util.List;
public final class DocumentsContract {
private static final String TAG = "Documents";
- // content://com.example/docs/12/
- // content://com.example/docs/12/children/
- // content://com.example/docs/12/search/?query=pony
+ // content://com.example/root/
+ // content://com.example/root/sdcard/
+ // content://com.example/root/sdcard/recent/
+ // content://com.example/document/12/
+ // content://com.example/document/12/children/
+ // content://com.example/document/12/search/?query=pony
private DocumentsContract() {
}
@@ -75,279 +74,297 @@ public final class DocumentsContract {
/** {@hide} */
public static final String ACTION_MANAGE_DOCUMENTS = "android.provider.action.MANAGE_DOCUMENTS";
- /** {@hide} */
- public static final String
- ACTION_DOCUMENT_ROOT_CHANGED = "android.provider.action.DOCUMENT_ROOT_CHANGED";
-
/**
- * Constants for individual documents.
+ * Constants related to a document, including {@link Cursor} columns names
+ * and flags.
+ * <p>
+ * A document can be either an openable file (with a specific MIME type), or
+ * a directory containing additional documents (with the
+ * {@link #MIME_TYPE_DIR} MIME type).
+ * <p>
+ * All columns are <em>read-only</em> to client applications.
*/
- public final static class Documents {
- private Documents() {
+ public final static class Document {
+ private Document() {
}
/**
- * MIME type of a document which is a directory that may contain additional
- * documents.
+ * Unique ID of a document. This ID is both provided by and interpreted
+ * by a {@link DocumentsProvider}, and should be treated as an opaque
+ * value by client applications.
+ * <p>
+ * Each document must have a unique ID within a provider, but that
+ * single document may be included as a child of multiple directories.
+ * <p>
+ * A provider must always return durable IDs, since they will be used to
+ * issue long-term Uri permission grants when an application interacts
+ * with {@link Intent#ACTION_OPEN_DOCUMENT} and
+ * {@link Intent#ACTION_CREATE_DOCUMENT}.
+ * <p>
+ * Type: STRING
*/
- public static final String MIME_TYPE_DIR = "vnd.android.doc/dir";
+ public static final String COLUMN_DOCUMENT_ID = "document_id";
/**
- * Flag indicating that a document is a directory that supports creation of
- * new files within it.
+ * Concrete MIME type of a document. For example, "image/png" or
+ * "application/pdf" for openable files. A document can also be a
+ * directory containing additional documents, which is represented with
+ * the {@link #MIME_TYPE_DIR} MIME type.
+ * <p>
+ * Type: STRING
*
- * @see DocumentColumns#FLAGS
+ * @see #MIME_TYPE_DIR
*/
- public static final int FLAG_SUPPORTS_CREATE = 1;
+ public static final String COLUMN_MIME_TYPE = "mime_type";
/**
- * Flag indicating that a document is renamable.
+ * Display name of a document, used as the primary title displayed to a
+ * user.
+ * <p>
+ * Type: STRING
+ */
+ public static final String COLUMN_DISPLAY_NAME = OpenableColumns.DISPLAY_NAME;
+
+ /**
+ * Summary of a document, which may be shown to a user. The summary may
+ * be {@code null}.
+ * <p>
+ * Type: STRING
+ */
+ public static final String COLUMN_SUMMARY = "summary";
+
+ /**
+ * Timestamp when a document was last modified, in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC, or {@code null} if unknown. A
+ * {@link DocumentsProvider} can update this field using events from
+ * {@link OnCloseListener} or other reliable
+ * {@link ParcelFileDescriptor} transports.
+ * <p>
+ * Type: INTEGER (long)
*
- * @see DocumentColumns#FLAGS
+ * @see System#currentTimeMillis()
*/
- public static final int FLAG_SUPPORTS_RENAME = 1 << 1;
+ public static final String COLUMN_LAST_MODIFIED = "last_modified";
/**
- * Flag indicating that a document is deletable.
+ * Specific icon resource ID for a document, or {@code null} to use
+ * platform default icon based on {@link #COLUMN_MIME_TYPE}.
+ * <p>
+ * Type: INTEGER (int)
+ */
+ public static final String COLUMN_ICON = "icon";
+
+ /**
+ * Flags that apply to a document.
+ * <p>
+ * Type: INTEGER (int)
*
- * @see DocumentColumns#FLAGS
+ * @see #FLAG_SUPPORTS_WRITE
+ * @see #FLAG_SUPPORTS_DELETE
+ * @see #FLAG_SUPPORTS_THUMBNAIL
+ * @see #FLAG_DIR_PREFERS_GRID
+ * @see #FLAG_DIR_SUPPORTS_CREATE
+ * @see #FLAG_DIR_SUPPORTS_SEARCH
*/
- public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
+ public static final String COLUMN_FLAGS = "flags";
/**
- * Flag indicating that a document can be represented as a thumbnail.
+ * Size of a document, in bytes, or {@code null} if unknown.
+ * <p>
+ * Type: INTEGER (long)
+ */
+ public static final String COLUMN_SIZE = OpenableColumns.SIZE;
+
+ /**
+ * MIME type of a document which is a directory that may contain
+ * additional documents.
*
- * @see DocumentColumns#FLAGS
+ * @see #COLUMN_MIME_TYPE
*/
- public static final int FLAG_SUPPORTS_THUMBNAIL = 1 << 3;
+ public static final String MIME_TYPE_DIR = "vnd.android.document/directory";
/**
- * Flag indicating that a document is a directory that supports search.
+ * Flag indicating that a document can be represented as a thumbnail.
*
- * @see DocumentColumns#FLAGS
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri,
+ * Point, CancellationSignal)
+ * @see DocumentsProvider#openDocumentThumbnail(String, Point,
+ * android.os.CancellationSignal)
*/
- public static final int FLAG_SUPPORTS_SEARCH = 1 << 4;
+ public static final int FLAG_SUPPORTS_THUMBNAIL = 1;
/**
* Flag indicating that a document supports writing.
+ * <p>
+ * When a document is opened with {@link Intent#ACTION_OPEN_DOCUMENT},
+ * the calling application is granted both
+ * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and
+ * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. However, the actual
+ * writability of a document may change over time, for example due to
+ * remote access changes. This flag indicates that a document client can
+ * expect {@link ContentResolver#openOutputStream(Uri)} to succeed.
*
- * @see DocumentColumns#FLAGS
+ * @see #COLUMN_FLAGS
*/
- public static final int FLAG_SUPPORTS_WRITE = 1 << 5;
+ public static final int FLAG_SUPPORTS_WRITE = 1 << 1;
/**
- * Flag indicating that a document is a directory that prefers its contents
- * be shown in a larger format grid. Usually suitable when a directory
- * contains mostly pictures.
+ * Flag indicating that a document is deletable.
*
- * @see DocumentColumns#FLAGS
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
+ * @see DocumentsProvider#deleteDocument(String)
*/
- public static final int FLAG_PREFERS_GRID = 1 << 6;
- }
-
- /**
- * Extra boolean flag included in a directory {@link Cursor#getExtras()}
- * indicating that a document provider is still loading data. For example, a
- * provider has returned some results, but is still waiting on an
- * outstanding network request.
- *
- * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver,
- * boolean)
- */
- public static final String EXTRA_LOADING = "loading";
-
- /**
- * Extra string included in a directory {@link Cursor#getExtras()}
- * providing an informational message that should be shown to a user. For
- * example, a provider may wish to indicate that not all documents are
- * available.
- */
- public static final String EXTRA_INFO = "info";
-
- /**
- * Extra string included in a directory {@link Cursor#getExtras()} providing
- * an error message that should be shown to a user. For example, a provider
- * may wish to indicate that a network error occurred. The user may choose
- * to retry, resulting in a new query.
- */
- public static final String EXTRA_ERROR = "error";
-
- /** {@hide} */
- public static final String METHOD_GET_ROOTS = "android:getRoots";
- /** {@hide} */
- public static final String METHOD_CREATE_DOCUMENT = "android:createDocument";
- /** {@hide} */
- public static final String METHOD_RENAME_DOCUMENT = "android:renameDocument";
- /** {@hide} */
- public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
-
- /** {@hide} */
- public static final String EXTRA_AUTHORITY = "authority";
- /** {@hide} */
- public static final String EXTRA_PACKAGE_NAME = "packageName";
- /** {@hide} */
- public static final String EXTRA_URI = "uri";
- /** {@hide} */
- public static final String EXTRA_ROOTS = "roots";
- /** {@hide} */
- public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
-
- private static final String PATH_DOCS = "docs";
- private static final String PATH_CHILDREN = "children";
- private static final String PATH_SEARCH = "search";
-
- private static final String PARAM_QUERY = "query";
+ public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
- /**
- * Build Uri representing the given {@link DocumentColumns#DOC_ID} in a
- * document provider.
- */
- public static Uri buildDocumentUri(String authority, String docId) {
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority).appendPath(PATH_DOCS).appendPath(docId).build();
- }
+ /**
+ * Flag indicating that a document is a directory that supports creation
+ * of new files within it. Only valid when {@link #COLUMN_MIME_TYPE} is
+ * {@link #MIME_TYPE_DIR}.
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#createDocument(ContentResolver, Uri, String,
+ * String)
+ * @see DocumentsProvider#createDocument(String, String, String)
+ */
+ public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3;
- /**
- * Build Uri representing the contents of the given directory in a document
- * provider. The given document must be {@link Documents#MIME_TYPE_DIR}.
- *
- * @hide
- */
- public static Uri buildChildrenUri(String authority, String docId) {
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
- .appendPath(PATH_DOCS).appendPath(docId).appendPath(PATH_CHILDREN).build();
- }
+ /**
+ * Flag indicating that a directory supports search. Only valid when
+ * {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsProvider#querySearchDocuments(String, String,
+ * String[])
+ */
+ public static final int FLAG_DIR_SUPPORTS_SEARCH = 1 << 4;
- /**
- * Build Uri representing a search for matching documents under a specific
- * directory in a document provider. The given document must have
- * {@link Documents#FLAG_SUPPORTS_SEARCH}.
- *
- * @hide
- */
- public static Uri buildSearchUri(String authority, String docId, String query) {
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
- .appendPath(PATH_DOCS).appendPath(docId).appendPath(PATH_SEARCH)
- .appendQueryParameter(PARAM_QUERY, query).build();
+ /**
+ * Flag indicating that a directory prefers its contents be shown in a
+ * larger format grid. Usually suitable when a directory contains mostly
+ * pictures. Only valid when {@link #COLUMN_MIME_TYPE} is
+ * {@link #MIME_TYPE_DIR}.
+ *
+ * @see #COLUMN_FLAGS
+ */
+ public static final int FLAG_DIR_PREFERS_GRID = 1 << 5;
}
/**
- * Extract the {@link DocumentColumns#DOC_ID} from the given Uri.
+ * Constants related to a root of documents, including {@link Cursor}
+ * columns names and flags.
+ * <p>
+ * All columns are <em>read-only</em> to client applications.
*/
- public static String getDocId(Uri documentUri) {
- final List<String> paths = documentUri.getPathSegments();
- if (paths.size() < 2) {
- throw new IllegalArgumentException("Not a document: " + documentUri);
+ public final static class Root {
+ private Root() {
}
- if (!PATH_DOCS.equals(paths.get(0))) {
- throw new IllegalArgumentException("Not a document: " + documentUri);
- }
- return paths.get(1);
- }
-
- /** {@hide} */
- public static String getSearchQuery(Uri documentUri) {
- return documentUri.getQueryParameter(PARAM_QUERY);
- }
- /**
- * Standard columns for document queries. Document providers <em>must</em>
- * support at least these columns when queried.
- */
- public interface DocumentColumns extends OpenableColumns {
/**
- * Unique ID for a document. Values <em>must</em> never change once
- * returned, since they may used for long-term Uri permission grants.
+ * Unique ID of a root. This ID is both provided by and interpreted by a
+ * {@link DocumentsProvider}, and should be treated as an opaque value
+ * by client applications.
* <p>
* Type: STRING
*/
- public static final String DOC_ID = "doc_id";
+ public static final String COLUMN_ROOT_ID = "root_id";
/**
- * MIME type of a document.
+ * Type of a root, used for clustering when presenting multiple roots to
+ * a user.
* <p>
- * Type: STRING
+ * Type: INTEGER (int)
*
- * @see Documents#MIME_TYPE_DIR
+ * @see #ROOT_TYPE_SERVICE
+ * @see #ROOT_TYPE_SHORTCUT
+ * @see #ROOT_TYPE_DEVICE
*/
- public static final String MIME_TYPE = "mime_type";
+ public static final String COLUMN_ROOT_TYPE = "root_type";
/**
- * Timestamp when a document was last modified, in milliseconds since
- * January 1, 1970 00:00:00.0 UTC, or {@code null} if unknown. Document
- * providers can update this field using events from
- * {@link OnCloseListener} or other reliable
- * {@link ParcelFileDescriptor} transports.
+ * Flags that apply to a root.
* <p>
- * Type: INTEGER (long)
+ * Type: INTEGER (int)
*
- * @see System#currentTimeMillis()
+ * @see #FLAG_LOCAL_ONLY
+ * @see #FLAG_SUPPORTS_CREATE
+ * @see #FLAG_ADVANCED
+ * @see #FLAG_PROVIDES_AUDIO
+ * @see #FLAG_PROVIDES_IMAGES
+ * @see #FLAG_PROVIDES_VIDEO
*/
- public static final String LAST_MODIFIED = "last_modified";
+ public static final String COLUMN_FLAGS = "flags";
/**
- * Specific icon resource for a document, or {@code null} to resolve
- * default using {@link #MIME_TYPE}.
+ * Icon resource ID for a root.
* <p>
* Type: INTEGER (int)
*/
- public static final String ICON = "icon";
+ public static final String COLUMN_ICON = "icon";
/**
- * Summary for a document, or {@code null} to omit.
+ * Title for a root, which will be shown to a user.
* <p>
* Type: STRING
*/
- public static final String SUMMARY = "summary";
+ public static final String COLUMN_TITLE = "title";
/**
- * Flags that apply to a specific document.
+ * Summary for this root, which may be shown to a user. The summary may
+ * be {@code null}.
* <p>
- * Type: INTEGER (int)
+ * Type: STRING
*/
- public static final String FLAGS = "flags";
- }
+ public static final String COLUMN_SUMMARY = "summary";
- /**
- * Metadata about a specific root of documents.
- */
- public final static class DocumentRoot implements Parcelable {
/**
- * Root that represents a storage service, such as a cloud-based
- * service.
+ * Document which is a directory that represents the top directory of
+ * this root.
+ * <p>
+ * Type: STRING
*
- * @see #rootType
+ * @see Document#COLUMN_DOCUMENT_ID
*/
- public static final int ROOT_TYPE_SERVICE = 1;
+ public static final String COLUMN_DOCUMENT_ID = "document_id";
+
+ /**
+ * Number of bytes available in this root, or {@code null} if unknown or
+ * unbounded.
+ * <p>
+ * Type: INTEGER (long)
+ */
+ public static final String COLUMN_AVAILABLE_BYTES = "available_bytes";
/**
- * Root that represents a shortcut to content that may be available
- * elsewhere through another storage root.
+ * Type of root that represents a storage service, such as a cloud-based
+ * service.
*
- * @see #rootType
+ * @see #COLUMN_ROOT_TYPE
*/
- public static final int ROOT_TYPE_SHORTCUT = 2;
+ public static final int ROOT_TYPE_SERVICE = 1;
/**
- * Root that represents a physical storage device.
+ * Type of root that represents a shortcut to content that may be
+ * available elsewhere through another storage root.
*
- * @see #rootType
+ * @see #COLUMN_ROOT_TYPE
*/
- public static final int ROOT_TYPE_DEVICE = 3;
+ public static final int ROOT_TYPE_SHORTCUT = 2;
/**
- * Root that represents a physical storage device that should only be
- * displayed to advanced users.
+ * Type of root that represents a physical storage device.
*
- * @see #rootType
+ * @see #COLUMN_ROOT_TYPE
*/
- public static final int ROOT_TYPE_DEVICE_ADVANCED = 4;
+ public static final int ROOT_TYPE_DEVICE = 3;
/**
* Flag indicating that at least one directory under this root supports
- * creating content.
+ * creating content. Roots with this flag will be shown when an
+ * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}.
*
- * @see #flags
+ * @see #COLUMN_FLAGS
*/
public static final int FLAG_SUPPORTS_CREATE = 1;
@@ -355,138 +372,201 @@ public final class DocumentsContract {
* Flag indicating that this root offers content that is strictly local
* on the device. That is, no network requests are made for the content.
*
- * @see #flags
+ * @see #COLUMN_FLAGS
+ * @see Intent#EXTRA_LOCAL_ONLY
*/
public static final int FLAG_LOCAL_ONLY = 1 << 1;
- /** {@hide} */
- public String authority;
-
/**
- * Root type, use for clustering.
+ * Flag indicating that this root should only be visible to advanced
+ * users.
*
- * @see #ROOT_TYPE_SERVICE
- * @see #ROOT_TYPE_DEVICE
+ * @see #COLUMN_FLAGS
*/
- public int rootType;
+ public static final int FLAG_ADVANCED = 1 << 2;
/**
- * Flags for this root.
+ * Flag indicating that a root offers audio documents. When a user is
+ * selecting audio, roots not providing audio may be excluded.
*
- * @see #FLAG_LOCAL_ONLY
+ * @see #COLUMN_FLAGS
+ * @see Intent#EXTRA_MIME_TYPES
*/
- public int flags;
+ public static final int FLAG_PROVIDES_AUDIO = 1 << 3;
/**
- * Icon resource ID for this root.
+ * Flag indicating that a root offers video documents. When a user is
+ * selecting video, roots not providing video may be excluded.
+ *
+ * @see #COLUMN_FLAGS
+ * @see Intent#EXTRA_MIME_TYPES
*/
- public int icon;
+ public static final int FLAG_PROVIDES_VIDEO = 1 << 4;
/**
- * Title for this root.
+ * Flag indicating that a root offers image documents. When a user is
+ * selecting images, roots not providing images may be excluded.
+ *
+ * @see #COLUMN_FLAGS
+ * @see Intent#EXTRA_MIME_TYPES
*/
- public String title;
+ public static final int FLAG_PROVIDES_IMAGES = 1 << 5;
+ }
- /**
- * Summary for this root. May be {@code null}.
- */
- public String summary;
+ /**
+ * Optional boolean flag included in a directory {@link Cursor#getExtras()}
+ * indicating that a document provider is still loading data. For example, a
+ * provider has returned some results, but is still waiting on an
+ * outstanding network request. The provider must send a content changed
+ * notification when loading is finished.
+ *
+ * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver,
+ * boolean)
+ */
+ public static final String EXTRA_LOADING = "loading";
- /**
- * Document which is a directory that represents the top of this root.
- * Must not be {@code null}.
- *
- * @see DocumentColumns#DOC_ID
- */
- public String docId;
+ /**
+ * Optional string included in a directory {@link Cursor#getExtras()}
+ * providing an informational message that should be shown to a user. For
+ * example, a provider may wish to indicate that not all documents are
+ * available.
+ */
+ public static final String EXTRA_INFO = "info";
- /**
- * Document which is a directory representing recently modified
- * documents under this root. This directory should return at most two
- * dozen documents modified within the last 90 days. May be {@code null}
- * if this root doesn't support recents.
- *
- * @see DocumentColumns#DOC_ID
- */
- public String recentDocId;
+ /**
+ * Optional string included in a directory {@link Cursor#getExtras()}
+ * providing an error message that should be shown to a user. For example, a
+ * provider may wish to indicate that a network error occurred. The user may
+ * choose to retry, resulting in a new query.
+ */
+ public static final String EXTRA_ERROR = "error";
- /**
- * Number of free bytes of available in this root, or -1 if unknown or
- * unbounded.
- */
- public long availableBytes;
+ /** {@hide} */
+ public static final String METHOD_CREATE_DOCUMENT = "android:createDocument";
+ /** {@hide} */
+ public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
- /**
- * Set of MIME type filters describing the content offered by this root,
- * or {@code null} to indicate that all MIME types are supported. For
- * example, a provider only supporting audio and video might set this to
- * {@code ["audio/*", "video/*"]}.
- */
- public String[] mimeTypes;
+ /** {@hide} */
+ public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
- public DocumentRoot() {
- }
+ private static final String PATH_ROOT = "root";
+ private static final String PATH_RECENT = "recent";
+ private static final String PATH_DOCUMENT = "document";
+ private static final String PATH_CHILDREN = "children";
+ private static final String PATH_SEARCH = "search";
- /** {@hide} */
- public DocumentRoot(Parcel in) {
- rootType = in.readInt();
- flags = in.readInt();
- icon = in.readInt();
- title = in.readString();
- summary = in.readString();
- docId = in.readString();
- recentDocId = in.readString();
- availableBytes = in.readLong();
- mimeTypes = in.readStringArray();
- }
+ private static final String PARAM_QUERY = "query";
- /** {@hide} */
- public Drawable loadIcon(Context context) {
- if (icon != 0) {
- if (authority != null) {
- final PackageManager pm = context.getPackageManager();
- final ProviderInfo info = pm.resolveContentProvider(authority, 0);
- if (info != null) {
- return pm.getDrawable(info.packageName, icon, info.applicationInfo);
- }
- } else {
- return context.getResources().getDrawable(icon);
- }
- }
- return null;
- }
+ /**
+ * Build Uri representing the roots of a document provider. When queried, a
+ * provider will return one or more rows with columns defined by
+ * {@link Root}.
+ *
+ * @see DocumentsProvider#queryRoots(String[])
+ */
+ public static Uri buildRootsUri(String authority) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).appendPath(PATH_ROOT).build();
+ }
- @Override
- public int describeContents() {
- return 0;
- }
+ /**
+ * Build Uri representing the recently modified documents of a specific
+ * root. When queried, a provider will return zero or more rows with columns
+ * defined by {@link Document}.
+ *
+ * @see DocumentsProvider#queryRecentDocuments(String, String[])
+ * @see #getRootId(Uri)
+ */
+ public static Uri buildRecentDocumentsUri(String authority, String rootId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).appendPath(PATH_ROOT).appendPath(rootId)
+ .appendPath(PATH_RECENT).build();
+ }
+
+ /**
+ * Build Uri representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
+ * document provider. When queried, a provider will return a single row with
+ * columns defined by {@link Document}.
+ *
+ * @see DocumentsProvider#queryDocument(String, String[])
+ * @see #getDocumentId(Uri)
+ */
+ public static Uri buildDocumentUri(String authority, String documentId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).appendPath(PATH_DOCUMENT).appendPath(documentId).build();
+ }
+
+ /**
+ * Build Uri representing the children of the given directory in a document
+ * provider. When queried, a provider will return zero or more rows with
+ * columns defined by {@link Document}.
+ *
+ * @param parentDocumentId the document to return children for, which must
+ * be a directory with MIME type of
+ * {@link Document#MIME_TYPE_DIR}.
+ * @see DocumentsProvider#queryChildDocuments(String, String[], String)
+ * @see #getDocumentId(Uri)
+ */
+ public static Uri buildChildDocumentsUri(String authority, String parentDocumentId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
+ .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_CHILDREN)
+ .build();
+ }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- Preconditions.checkNotNull(docId);
-
- dest.writeInt(rootType);
- dest.writeInt(flags);
- dest.writeInt(icon);
- dest.writeString(title);
- dest.writeString(summary);
- dest.writeString(docId);
- dest.writeString(recentDocId);
- dest.writeLong(availableBytes);
- dest.writeStringArray(mimeTypes);
+ /**
+ * Build Uri representing a search for matching documents under a specific
+ * directory in a document provider. When queried, a provider will return
+ * zero or more rows with columns defined by {@link Document}.
+ *
+ * @param parentDocumentId the document to return children for, which must
+ * be both a directory with MIME type of
+ * {@link Document#MIME_TYPE_DIR} and have
+ * {@link Document#FLAG_DIR_SUPPORTS_SEARCH} set.
+ * @see DocumentsProvider#querySearchDocuments(String, String, String[])
+ * @see #getDocumentId(Uri)
+ * @see #getSearchDocumentsQuery(Uri)
+ */
+ public static Uri buildSearchDocumentsUri(
+ String authority, String parentDocumentId, String query) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
+ .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_SEARCH)
+ .appendQueryParameter(PARAM_QUERY, query).build();
+ }
+
+ /**
+ * Extract the {@link Root#COLUMN_ROOT_ID} from the given Uri.
+ */
+ public static String getRootId(Uri rootUri) {
+ final List<String> paths = rootUri.getPathSegments();
+ if (paths.size() < 2) {
+ throw new IllegalArgumentException("Not a root: " + rootUri);
+ }
+ if (!PATH_ROOT.equals(paths.get(0))) {
+ throw new IllegalArgumentException("Not a root: " + rootUri);
}
+ return paths.get(1);
+ }
- public static final Creator<DocumentRoot> CREATOR = new Creator<DocumentRoot>() {
- @Override
- public DocumentRoot createFromParcel(Parcel in) {
- return new DocumentRoot(in);
- }
+ /**
+ * Extract the {@link Document#COLUMN_DOCUMENT_ID} from the given Uri.
+ */
+ public static String getDocumentId(Uri documentUri) {
+ final List<String> paths = documentUri.getPathSegments();
+ if (paths.size() < 2) {
+ throw new IllegalArgumentException("Not a document: " + documentUri);
+ }
+ if (!PATH_DOCUMENT.equals(paths.get(0))) {
+ throw new IllegalArgumentException("Not a document: " + documentUri);
+ }
+ return paths.get(1);
+ }
- @Override
- public DocumentRoot[] newArray(int size) {
- return new DocumentRoot[size];
- }
- };
+ /**
+ * Extract the search query from a Uri built by
+ * {@link #buildSearchDocumentsUri(String, String, String)}.
+ */
+ public static String getSearchDocumentsQuery(Uri searchDocumentsUri) {
+ return searchDocumentsUri.getQueryParameter(PARAM_QUERY);
}
/**
@@ -497,6 +577,7 @@ public final class DocumentsContract {
* {@link Intent#ACTION_CREATE_DOCUMENT}.
*
* @see Context#grantUriPermission(String, Uri, int)
+ * @see Context#revokeUriPermission(Uri, int)
* @see ContentResolver#getIncomingUriPermissionGrants(int, int)
*/
public static Uri[] getOpenDocuments(Context context) {
@@ -520,20 +601,28 @@ public final class DocumentsContract {
}
/**
- * Return thumbnail representing the document at the given URI. Callers are
- * responsible for their own in-memory caching. Given document must have
- * {@link Documents#FLAG_SUPPORTS_THUMBNAIL} set.
+ * Return thumbnail representing the document at the given Uri. Callers are
+ * responsible for their own in-memory caching.
*
+ * @param documentUri document to return thumbnail for, which must have
+ * {@link Document#FLAG_SUPPORTS_THUMBNAIL} set.
+ * @param size optimal thumbnail size desired. A provider may return a
+ * thumbnail of a different size, but never more than double the
+ * requested size.
+ * @param signal signal used to indicate that caller is no longer interested
+ * in the thumbnail.
* @return decoded thumbnail, or {@code null} if problem was encountered.
- * @hide
+ * @see DocumentsProvider#openDocumentThumbnail(String, Point,
+ * android.os.CancellationSignal)
*/
- public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) {
+ public static Bitmap getDocumentThumbnail(
+ ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal) {
final Bundle openOpts = new Bundle();
openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
AssetFileDescriptor afd = null;
try {
- afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts);
+ afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
final FileDescriptor fd = afd.getFileDescriptor();
final long offset = afd.getStartOffset();
@@ -583,38 +672,26 @@ public final class DocumentsContract {
}
}
- /** {@hide} */
- public static List<DocumentRoot> getDocumentRoots(ContentProviderClient client) {
- try {
- final Bundle out = client.call(METHOD_GET_ROOTS, null, null);
- final List<DocumentRoot> roots = out.getParcelableArrayList(EXTRA_ROOTS);
- return roots;
- } catch (Exception e) {
- Log.w(TAG, "Failed to get roots", e);
- return null;
- }
- }
-
/**
- * Create a new document under the given parent document with MIME type and
- * display name.
+ * Create a new document with given MIME type and display name.
*
- * @param docId document with {@link Documents#FLAG_SUPPORTS_CREATE}
+ * @param parentDocumentUri directory with
+ * {@link Document#FLAG_DIR_SUPPORTS_CREATE}
* @param mimeType MIME type of new document
* @param displayName name of new document
* @return newly created document, or {@code null} if failed
- * @hide
*/
- public static String createDocument(
- ContentProviderClient client, String docId, String mimeType, String displayName) {
+ public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri,
+ String mimeType, String displayName) {
final Bundle in = new Bundle();
- in.putString(DocumentColumns.DOC_ID, docId);
- in.putString(DocumentColumns.MIME_TYPE, mimeType);
- in.putString(DocumentColumns.DISPLAY_NAME, displayName);
+ in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(parentDocumentUri));
+ in.putString(Document.COLUMN_MIME_TYPE, mimeType);
+ in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
try {
- final Bundle out = client.call(METHOD_CREATE_DOCUMENT, null, in);
- return out.getString(DocumentColumns.DOC_ID);
+ final Bundle out = resolver.call(parentDocumentUri, METHOD_CREATE_DOCUMENT, null, in);
+ return buildDocumentUri(
+ parentDocumentUri.getAuthority(), out.getString(Document.COLUMN_DOCUMENT_ID));
} catch (Exception e) {
Log.w(TAG, "Failed to create document", e);
return null;
@@ -622,40 +699,16 @@ public final class DocumentsContract {
}
/**
- * Rename the given document.
- *
- * @param docId document with {@link Documents#FLAG_SUPPORTS_RENAME}
- * @return document which may have changed due to rename, or {@code null} if
- * rename failed.
- * @hide
- */
- public static String renameDocument(
- ContentProviderClient client, String docId, String displayName) {
- final Bundle in = new Bundle();
- in.putString(DocumentColumns.DOC_ID, docId);
- in.putString(DocumentColumns.DISPLAY_NAME, displayName);
-
- try {
- final Bundle out = client.call(METHOD_RENAME_DOCUMENT, null, in);
- return out.getString(DocumentColumns.DOC_ID);
- } catch (Exception e) {
- Log.w(TAG, "Failed to rename document", e);
- return null;
- }
- }
-
- /**
* Delete the given document.
*
- * @param docId document with {@link Documents#FLAG_SUPPORTS_DELETE}
- * @hide
+ * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
*/
- public static boolean deleteDocument(ContentProviderClient client, String docId) {
+ public static boolean deleteDocument(ContentResolver resolver, Uri documentUri) {
final Bundle in = new Bundle();
- in.putString(DocumentColumns.DOC_ID, docId);
+ in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(documentUri));
try {
- client.call(METHOD_DELETE_DOCUMENT, null, in);
+ final Bundle out = resolver.call(documentUri, METHOD_DELETE_DOCUMENT, null, in);
return true;
} catch (Exception e) {
Log.w(TAG, "Failed to delete document", e);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index eeb8c41..09f4866 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -16,16 +16,12 @@
package android.provider;
-import static android.provider.DocumentsContract.ACTION_DOCUMENT_ROOT_CHANGED;
-import static android.provider.DocumentsContract.EXTRA_AUTHORITY;
-import static android.provider.DocumentsContract.EXTRA_ROOTS;
import static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE;
import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
-import static android.provider.DocumentsContract.METHOD_GET_ROOTS;
-import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
-import static android.provider.DocumentsContract.getDocId;
-import static android.provider.DocumentsContract.getSearchQuery;
+import static android.provider.DocumentsContract.getDocumentId;
+import static android.provider.DocumentsContract.getRootId;
+import static android.provider.DocumentsContract.getSearchDocumentsQuery;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -41,15 +37,12 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
-import android.provider.DocumentsContract.DocumentColumns;
-import android.provider.DocumentsContract.DocumentRoot;
-import android.provider.DocumentsContract.Documents;
+import android.provider.DocumentsContract.Document;
import android.util.Log;
import libcore.io.IoUtils;
import java.io.FileNotFoundException;
-import java.util.List;
/**
* Base class for a document provider. A document provider should extend this
@@ -58,13 +51,13 @@ import java.util.List;
* Each document provider expresses one or more "roots" which each serve as the
* top-level of a tree. For example, a root could represent an account, or a
* physical storage device. Under each root, documents are referenced by
- * {@link DocumentColumns#DOC_ID}, which must not change once returned.
+ * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned.
* <p>
* Documents can be either an openable file (with a specific MIME type), or a
* directory containing additional documents (with the
- * {@link Documents#MIME_TYPE_DIR} MIME type). Each document can have different
- * capabilities, as described by {@link DocumentColumns#FLAGS}. The same
- * {@link DocumentColumns#DOC_ID} can be included in multiple directories.
+ * {@link Document#MIME_TYPE_DIR} MIME type). Each document can have different
+ * capabilities, as described by {@link Document#COLUMN_FLAGS}. The same
+ * {@link Document#COLUMN_DOCUMENT_ID} can be included in multiple directories.
* <p>
* Document providers must be protected with the
* {@link android.Manifest.permission#MANAGE_DOCUMENTS} permission, which can
@@ -78,22 +71,29 @@ import java.util.List;
public abstract class DocumentsProvider extends ContentProvider {
private static final String TAG = "DocumentsProvider";
- private static final int MATCH_DOCUMENT = 1;
- private static final int MATCH_CHILDREN = 2;
- private static final int MATCH_SEARCH = 3;
+ private static final int MATCH_ROOT = 1;
+ private static final int MATCH_RECENT = 2;
+ private static final int MATCH_DOCUMENT = 3;
+ private static final int MATCH_CHILDREN = 4;
+ private static final int MATCH_SEARCH = 5;
private String mAuthority;
private UriMatcher mMatcher;
+ /**
+ * Implementation is provided by the parent class.
+ */
@Override
public void attachInfo(Context context, ProviderInfo info) {
mAuthority = info.authority;
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- mMatcher.addURI(mAuthority, "docs/*", MATCH_DOCUMENT);
- mMatcher.addURI(mAuthority, "docs/*/children", MATCH_CHILDREN);
- mMatcher.addURI(mAuthority, "docs/*/search", MATCH_SEARCH);
+ mMatcher.addURI(mAuthority, "root", MATCH_ROOT);
+ mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
+ mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
+ mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
+ mMatcher.addURI(mAuthority, "document/*/search", MATCH_SEARCH);
// Sanity check our setup
if (!info.exported) {
@@ -111,83 +111,80 @@ public abstract class DocumentsProvider extends ContentProvider {
}
/**
- * Return list of all document roots provided by this document provider.
- * When this list changes, a provider must call
- * {@link #notifyDocumentRootsChanged()}.
- */
- public abstract List<DocumentRoot> getDocumentRoots();
-
- /**
- * Create and return a new document. A provider must allocate a new
- * {@link DocumentColumns#DOC_ID} to represent the document, which must not
- * change once returned.
+ * Create a new document and return its {@link Document#COLUMN_DOCUMENT_ID}.
+ * A provider must allocate a new {@link Document#COLUMN_DOCUMENT_ID} to
+ * represent the document, which must not change once returned.
*
- * @param docId the parent directory to create the new document under.
+ * @param documentId the parent directory to create the new document under.
* @param mimeType the MIME type associated with the new document.
* @param displayName the display name of the new document.
*/
@SuppressWarnings("unused")
- public String createDocument(String docId, String mimeType, String displayName)
+ public String createDocument(String documentId, String mimeType, String displayName)
throws FileNotFoundException {
throw new UnsupportedOperationException("Create not supported");
}
/**
- * Rename the given document.
+ * Delete the given document. Upon returning, any Uri permission grants for
+ * the given document will be revoked. If additional documents were deleted
+ * as a side effect of this call, such as documents inside a directory, the
+ * implementor is responsible for revoking those permissions.
*
- * @param docId the document to rename.
- * @param displayName the new display name.
+ * @param documentId the document to delete.
*/
@SuppressWarnings("unused")
- public void renameDocument(String docId, String displayName) throws FileNotFoundException {
- throw new UnsupportedOperationException("Rename not supported");
+ public void deleteDocument(String documentId) throws FileNotFoundException {
+ throw new UnsupportedOperationException("Delete not supported");
}
- /**
- * Delete the given document.
- *
- * @param docId the document to delete.
- */
+ public abstract Cursor queryRoots(String[] projection) throws FileNotFoundException;
+
@SuppressWarnings("unused")
- public void deleteDocument(String docId) throws FileNotFoundException {
- throw new UnsupportedOperationException("Delete not supported");
+ public Cursor queryRecentDocuments(String rootId, String[] projection)
+ throws FileNotFoundException {
+ throw new UnsupportedOperationException("Recent not supported");
}
/**
* Return metadata for the given document. A provider should avoid making
* network requests to keep this request fast.
*
- * @param docId the document to return.
+ * @param documentId the document to return.
*/
- public abstract Cursor queryDocument(String docId) throws FileNotFoundException;
+ public abstract Cursor queryDocument(String documentId, String[] projection)
+ throws FileNotFoundException;
/**
* Return the children of the given document which is a directory.
*
- * @param docId the directory to return children for.
+ * @param parentDocumentId the directory to return children for.
*/
- public abstract Cursor queryDocumentChildren(String docId) throws FileNotFoundException;
+ public abstract Cursor queryChildDocuments(
+ String parentDocumentId, String[] projection, String sortOrder)
+ throws FileNotFoundException;
/**
* Return documents that that match the given query, starting the search at
* the given directory.
*
- * @param docId the directory to start search at.
+ * @param parentDocumentId the directory to start search at.
*/
@SuppressWarnings("unused")
- public Cursor querySearch(String docId, String query) throws FileNotFoundException {
+ public Cursor querySearchDocuments(String parentDocumentId, String query, String[] projection)
+ throws FileNotFoundException {
throw new UnsupportedOperationException("Search not supported");
}
/**
* Return MIME type for the given document. Must match the value of
- * {@link DocumentColumns#MIME_TYPE} for this document.
+ * {@link Document#COLUMN_MIME_TYPE} for this document.
*/
- public String getType(String docId) throws FileNotFoundException {
- final Cursor cursor = queryDocument(docId);
+ public String getDocumentType(String documentId) throws FileNotFoundException {
+ final Cursor cursor = queryDocument(documentId, null);
try {
if (cursor.moveToFirst()) {
- return cursor.getString(cursor.getColumnIndexOrThrow(DocumentColumns.MIME_TYPE));
+ return cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE));
} else {
return null;
}
@@ -233,7 +230,7 @@ public abstract class DocumentsProvider extends ContentProvider {
* @param sizeHint hint of the optimal thumbnail dimensions.
* @param signal used by the caller to signal if the request should be
* cancelled.
- * @see Documents#FLAG_SUPPORTS_THUMBNAIL
+ * @see Document#FLAG_SUPPORTS_THUMBNAIL
*/
@SuppressWarnings("unused")
public AssetFileDescriptor openDocumentThumbnail(
@@ -241,17 +238,31 @@ public abstract class DocumentsProvider extends ContentProvider {
throw new UnsupportedOperationException("Thumbnails not supported");
}
+ /**
+ * Implementation is provided by the parent class. Cannot be overriden.
+ *
+ * @see #queryRoots(String[])
+ * @see #queryRecentDocuments(String, String[])
+ * @see #queryDocument(String, String[])
+ * @see #queryChildDocuments(String, String[], String)
+ * @see #querySearchDocuments(String, String, String[])
+ */
@Override
- public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ public final Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
try {
switch (mMatcher.match(uri)) {
+ case MATCH_ROOT:
+ return queryRoots(projection);
+ case MATCH_RECENT:
+ return queryRecentDocuments(getRootId(uri), projection);
case MATCH_DOCUMENT:
- return queryDocument(getDocId(uri));
+ return queryDocument(getDocumentId(uri), projection);
case MATCH_CHILDREN:
- return queryDocumentChildren(getDocId(uri));
+ return queryChildDocuments(getDocumentId(uri), projection, sortOrder);
case MATCH_SEARCH:
- return querySearch(getDocId(uri), getSearchQuery(uri));
+ return querySearchDocuments(
+ getDocumentId(uri), getSearchDocumentsQuery(uri), projection);
default:
throw new UnsupportedOperationException("Unsupported Uri " + uri);
}
@@ -261,12 +272,17 @@ public abstract class DocumentsProvider extends ContentProvider {
}
}
+ /**
+ * Implementation is provided by the parent class. Cannot be overriden.
+ *
+ * @see #getDocumentType(String)
+ */
@Override
public final String getType(Uri uri) {
try {
switch (mMatcher.match(uri)) {
case MATCH_DOCUMENT:
- return getType(getDocId(uri));
+ return getDocumentType(getDocumentId(uri));
default:
return null;
}
@@ -276,22 +292,39 @@ public abstract class DocumentsProvider extends ContentProvider {
}
}
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ *
+ * @see #createDocument(String, String, String)
+ */
@Override
public final Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("Insert not supported");
}
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ *
+ * @see #deleteDocument(String)
+ */
@Override
public final int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Delete not supported");
}
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
@Override
public final int update(
Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Update not supported");
}
+ /** {@hide} */
@Override
public final Bundle callFromPackage(
String callingPackage, String method, String arg, Bundle extras) {
@@ -300,33 +333,25 @@ public abstract class DocumentsProvider extends ContentProvider {
return super.callFromPackage(callingPackage, method, arg, extras);
}
- // Platform operations require the caller explicitly hold manage
- // permission; Uri permissions don't extend management operations.
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_DOCUMENTS, "Document management");
+ // Require that caller can manage given document
+ final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
+ final Uri documentUri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
+ getContext().enforceCallingOrSelfUriPermission(
+ documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method);
final Bundle out = new Bundle();
try {
- if (METHOD_GET_ROOTS.equals(method)) {
- final List<DocumentRoot> roots = getDocumentRoots();
- out.putParcelableList(EXTRA_ROOTS, roots);
-
- } else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- final String docId = extras.getString(DocumentColumns.DOC_ID);
- final String mimeType = extras.getString(DocumentColumns.MIME_TYPE);
- final String displayName = extras.getString(DocumentColumns.DISPLAY_NAME);
+ if (METHOD_CREATE_DOCUMENT.equals(method)) {
+ final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
+ final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
- // TODO: issue Uri grant towards caller
- final String newDocId = createDocument(docId, mimeType, displayName);
- out.putString(DocumentColumns.DOC_ID, newDocId);
-
- } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- final String docId = extras.getString(DocumentColumns.DOC_ID);
- final String displayName = extras.getString(DocumentColumns.DISPLAY_NAME);
- renameDocument(docId, displayName);
+ // TODO: issue Uri grant towards calling package
+ // TODO: enforce that package belongs to caller
+ final String newDocumentId = createDocument(documentId, mimeType, displayName);
+ out.putString(Document.COLUMN_DOCUMENT_ID, newDocumentId);
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- final String docId = extras.getString(DocumentColumns.DOC_ID);
+ final String docId = extras.getString(Document.COLUMN_DOCUMENT_ID);
deleteDocument(docId);
} else {
@@ -338,47 +363,57 @@ public abstract class DocumentsProvider extends ContentProvider {
return out;
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocument(String, String, CancellationSignal)
+ */
@Override
public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- return openDocument(getDocId(uri), mode, null);
+ return openDocument(getDocumentId(uri), mode, null);
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocument(String, String, CancellationSignal)
+ */
@Override
public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
throws FileNotFoundException {
- return openDocument(getDocId(uri), mode, signal);
+ return openDocument(getDocumentId(uri), mode, signal);
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocumentThumbnail(String, Point, CancellationSignal)
+ */
@Override
public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
- return openDocumentThumbnail(getDocId(uri), sizeHint, null);
+ return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
} else {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
}
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocumentThumbnail(String, Point, CancellationSignal)
+ */
@Override
public final AssetFileDescriptor openTypedAssetFile(
Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
- return openDocumentThumbnail(getDocId(uri), sizeHint, signal);
+ return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
} else {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
}
}
-
- /**
- * Notify system that {@link #getDocumentRoots()} has changed, usually due to an
- * account or device change.
- */
- public void notifyDocumentRootsChanged() {
- final Intent intent = new Intent(ACTION_DOCUMENT_ROOT_CHANGED);
- intent.putExtra(EXTRA_AUTHORITY, mAuthority);
- getContext().sendBroadcast(intent);
- }
}