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
This commit is contained in:
@@ -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).
|
||||
|
||||
207
core/java/android/provider/DocumentsContract.java
Normal file
207
core/java/android/provider/DocumentsContract.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user