diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-04-19 14:05:03 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-05-01 17:21:06 -0700 |
commit | 9ecfee03fa188aebfbd9778b4e020323903495ee (patch) | |
tree | ffc67525888fb5658f6e504c01b7bda6273621db | |
parent | 85c5f084e516994adcf08a6a244a2d1f0fcfe1d3 (diff) | |
download | frameworks_base-9ecfee03fa188aebfbd9778b4e020323903495ee.zip frameworks_base-9ecfee03fa188aebfbd9778b4e020323903495ee.tar.gz frameworks_base-9ecfee03fa188aebfbd9778b4e020323903495ee.tar.bz2 |
Start fleshing out new storage APIs.
Introduces new DocumentsContract which storage backends must
implement. Backends surface a simple directory-like organizational
structure that enables a document to appear at multiple locations in
that hierarchy. Querying a document or the contents of a directory
will return a Cursor populated with DocumentColumns, which includes
simple metadata.
Adds new OPEN_DOC and CREATE_DOC Intents, and permission to protect
storage backends.
Change-Id: Ib4984bc980182b2cedbe552908e5be94604ef085
-rw-r--r-- | api/current.txt | 25 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 48 | ||||
-rw-r--r-- | core/java/android/provider/DocumentsContract.java | 207 | ||||
-rw-r--r-- | core/java/android/provider/OpenableColumns.java | 14 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 8 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 5 |
6 files changed, 303 insertions, 4 deletions
diff --git a/api/current.txt b/api/current.txt index c1c550d..a9f0ef8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -70,6 +70,7 @@ package android { field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS"; field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS"; + field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR"; field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS"; field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE"; @@ -6047,6 +6048,7 @@ package android.content { field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER"; field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS"; field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED"; + field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT"; field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED"; field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW"; @@ -6091,6 +6093,7 @@ package android.content { field public static final java.lang.String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED"; field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED"; field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"; + field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT"; field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; @@ -19494,6 +19497,28 @@ package android.provider { field public static final android.net.Uri CONTENT_URI; } + public final class DocumentsContract { + ctor public DocumentsContract(); + method public static android.net.Uri buildContentsUri(android.net.Uri); + method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String); + method public static android.net.Uri buildSearchUri(java.lang.String, java.lang.String); + method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point); + method public static boolean renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String); + field public static final java.lang.String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; + field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 + field public static final int FLAG_SUPPORTS_RENAME = 2; // 0x2 + field public static final int FLAG_SUPPORTS_THUMBNAIL = 4; // 0x4 + field public static final java.lang.String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc"; + field public static final java.lang.String ROOT_GUID = "0"; + } + + public static abstract interface DocumentsContract.DocumentColumns implements android.provider.OpenableColumns { + field public static final java.lang.String FLAGS = "flags"; + field public static final java.lang.String GUID = "guid"; + field public static final java.lang.String LAST_MODIFIED = "last_modified"; + field public static final java.lang.String MIME_TYPE = "mime_type"; + } + public final deprecated class LiveFolders implements android.provider.BaseColumns { field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER"; field public static final java.lang.String DESCRIPTION = "description"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5fa1a6c..621bcee 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2591,6 +2591,46 @@ public class Intent implements Parcelable, Cloneable { */ public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON"; + /** + * Activity Action: Allow the user to select and open one or more existing + * documents. Both read and write access to the documents will be granted + * until explicitly revoked by the user. + * <p> + * Callers can restrict selection to a specific kind of data, such as + * photos, by setting one or more MIME types in {@link #EXTRA_MIME_TYPES}. + * <p> + * If the caller can handle multiple returned items (the user performing + * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} to + * indicate this. + * <p> + * All returned URIs can be opened as a stream with + * {@link ContentResolver#openInputStream(Uri)}. + * <p> + * Output: The URI of the item that was picked. This must be a content: URI + * so that any receiver can access it. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT"; + + /** + * Activity Action: Allow the user to create a new document. Both read and + * write access to the document will be granted until explicitly revoked by + * the user. + * <p> + * Callers can provide a hint document name by setting {@link #EXTRA_TITLE}, + * but the user may change this value before creating the file. Callers can + * optionally hint at the MIME type being created by setting + * {@link #setType(String)}. + * <p> + * All returned URIs can be opened as a stream with + * {@link ContentResolver#openOutputStream(Uri)}. + * <p> + * Output: The URI of the item that was created. This must be a content: URI + * so that any receiver can access it. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -3194,6 +3234,14 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; + /** + * Extra used to communicate set of acceptable MIME types for + * {@link #ACTION_GET_CONTENT} or {@link #ACTION_OPEN_DOC}. The type of the + * extra is <code>ArrayList<String></code>. + * @hide + */ + public static final String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java new file mode 100644 index 0000000..e10ead9 --- /dev/null +++ b/core/java/android/provider/DocumentsContract.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.provider; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.content.res.AssetFileDescriptor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Point; +import android.net.Uri; +import android.os.Bundle; +import android.os.SystemClock; +import android.util.Log; + +import libcore.io.IoUtils; + +import java.io.IOException; +import java.io.InputStream; + +/** + * The contract between a storage backend and the platform. Contains definitions + * for the supported URIs and columns. + */ +public final class DocumentsContract { + private static final String TAG = "Documents"; + + // content://com.example/docs/0/ + // content://com.example/docs/0/contents/ + // content://com.example/search/?query=pony + + /** + * MIME type of a document which is a directory that may contain additional + * documents. + * + * @see #buildContentsUri(Uri) + */ + public static final String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc"; + + /** + * {@link DocumentColumns#GUID} value representing the root directory of a + * storage backend. + */ + public static final String ROOT_GUID = "0"; + + /** + * Flag indicating that a document is a directory that supports creation of + * new files within it. + * + * @see DocumentColumns#FLAGS + * @see #buildContentsUri(Uri) + */ + public static final int FLAG_SUPPORTS_CREATE = 1; + + /** + * Flag indicating that a document is renamable. + * + * @see DocumentColumns#FLAGS + * @see #renameDocument(ContentResolver, Uri, String) + */ + public static final int FLAG_SUPPORTS_RENAME = 1 << 1; + + /** + * Flag indicating that a document can be represented as a thumbnail. + * + * @see DocumentColumns#FLAGS + * @see #getThumbnail(ContentResolver, Uri, Point) + */ + public static final int FLAG_SUPPORTS_THUMBNAIL = 1 << 2; + + /** + * Optimal dimensions for a document thumbnail request, stored as a + * {@link Point} object. This is only a hint, and the returned thumbnail may + * have different dimensions. + */ + public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; + + private static final String PATH_DOCS = "docs"; + private static final String PATH_CONTENTS = "contents"; + private static final String PATH_SEARCH = "search"; + + private static final String PARAM_QUERY = "query"; + + /** + * Build URI representing the given {@link DocumentColumns#GUID} in a + * storage backend. + */ + public static Uri buildDocumentUri(String authority, String guid) { + return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(authority).appendPath(PATH_DOCS).appendPath(guid).build(); + } + + /** + * Build URI representing a search for matching documents in a storage + * backend. + */ + public static Uri buildSearchUri(String authority, String query) { + return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) + .appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build(); + } + + /** + * Build URI representing the contents of the given directory in a storage + * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}. + */ + public static Uri buildContentsUri(Uri documentUri) { + return documentUri.buildUpon().appendPath(PATH_CONTENTS).build(); + } + + /** + * These are standard columns for document URIs. Storage backend providers + * <em>must</em> support at least these columns when queried. + * + * @see Intent#ACTION_OPEN_DOCUMENT + * @see Intent#ACTION_CREATE_DOCUMENT + */ + public interface DocumentColumns extends OpenableColumns { + /** + * The globally unique ID for a document within a storage backend. + * Values <em>must</em> never change once returned. + * <p> + * Type: STRING + * + * @see DocumentsContract#ROOT_GUID + */ + public static final String GUID = "guid"; + + /** + * MIME type of a document, matching the value returned by + * {@link ContentResolver#getType(android.net.Uri)}. + * <p> + * Type: STRING + * + * @see DocumentsContract#MIME_TYPE_DIRECTORY + */ + public static final String MIME_TYPE = "mime_type"; + + /** + * Timestamp when a document was last modified, in milliseconds since + * January 1, 1970 00:00:00.0 UTC. + * <p> + * Type: INTEGER (long) + * + * @see System#currentTimeMillis() + */ + public static final String LAST_MODIFIED = "last_modified"; + + /** + * Flags that apply to a specific document. + * <p> + * Type: INTEGER (int) + */ + public static final String FLAGS = "flags"; + } + + /** + * Return thumbnail representing the document at the given URI. Callers are + * responsible for their own caching. Given document must have + * {@link #FLAG_SUPPORTS_THUMBNAIL} set. + * + * @return decoded thumbnail, or {@code null} if problem was encountered. + */ + public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) { + final Bundle opts = new Bundle(); + opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size); + + InputStream is = null; + try { + is = new AssetFileDescriptor.AutoCloseInputStream( + resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts)); + return BitmapFactory.decodeStream(is); + } catch (IOException e) { + Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); + return null; + } finally { + IoUtils.closeQuietly(is); + } + } + + /** + * Rename the document at the given URI. Given document must have + * {@link #FLAG_SUPPORTS_RENAME} set. + * + * @return if rename was successful. + */ + public static boolean renameDocument( + ContentResolver resolver, Uri documentUri, String displayName) { + final ContentValues values = new ContentValues(); + values.put(DocumentColumns.DISPLAY_NAME, displayName); + return (resolver.update(documentUri, values, null, null) == 1); + } +} diff --git a/core/java/android/provider/OpenableColumns.java b/core/java/android/provider/OpenableColumns.java index f548bae..faf96b7 100644 --- a/core/java/android/provider/OpenableColumns.java +++ b/core/java/android/provider/OpenableColumns.java @@ -16,11 +16,17 @@ package android.provider; +import android.content.ContentResolver; +import android.content.Intent; + /** - * These are standard columns for openable URIs. (See - * {@link android.content.Intent#CATEGORY_OPENABLE}.) If possible providers that have openable URIs - * should support these columns. To find the content type of a URI use - * {@link android.content.ContentResolver#getType(android.net.Uri)} as normal. + * These are standard columns for openable URIs. Providers that serve openable + * URIs <em>must</em> support at least these columns when queried. + * <p> + * To find the content type of a URI, use + * {@link ContentResolver#getType(android.net.Uri)}. + * + * @see Intent#CATEGORY_OPENABLE */ public interface OpenableColumns { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 83d6061..8ef127a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1078,6 +1078,14 @@ android:description="@string/permdesc_mediaStorageWrite" android:protectionLevel="signature|system" /> + <!-- Allows an application to manage access to documents, usually as part + of a document picker. --> + <permission android:name="android.permission.MANAGE_DOCUMENTS" + android:permissionGroup="android.permission-group.STORAGE" + android:label="@string/permlab_manageDocs" + android:description="@string/permdesc_manageDocs" + android:protectionLevel="signature|system" /> + <!-- ================================== --> <!-- Permissions for screenlock --> <!-- ================================== --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 09be719..50fad5f 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1797,6 +1797,11 @@ <string name="permdesc_mediaStorageWrite" product="default">Allows the app to modify the contents of the internal media storage.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <string name="permlab_manageDocs" product="default">manage document storage</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permdesc_manageDocs" product="default">Allows the app to manage document storage.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> <string name="permlab_sdcardAccessAll">access external storage of all users</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_sdcardAccessAll">Allows the app to access external storage for all users.</string> |