DocumentsContract.java revision 66516697a29cf00d93893a1011d3befc7c1ee37f
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.provider; 18 19import android.content.ContentProvider; 20import android.content.ContentResolver; 21import android.content.ContentValues; 22import android.content.Intent; 23import android.content.pm.ProviderInfo; 24import android.content.res.AssetFileDescriptor; 25import android.database.Cursor; 26import android.graphics.Bitmap; 27import android.graphics.BitmapFactory; 28import android.graphics.Point; 29import android.net.Uri; 30import android.os.Bundle; 31import android.util.Log; 32 33import libcore.io.IoUtils; 34 35import java.io.IOException; 36import java.io.InputStream; 37import java.util.List; 38 39/** 40 * The contract between a storage backend and the platform. Contains definitions 41 * for the supported URIs and columns. 42 */ 43public final class DocumentsContract { 44 private static final String TAG = "Documents"; 45 46 // content://com.example/roots/ 47 // content://com.example/roots/sdcard/ 48 // content://com.example/roots/sdcard/docs/0/ 49 // content://com.example/roots/sdcard/docs/0/contents/ 50 // content://com.example/roots/sdcard/docs/0/search/?query=pony 51 52 /** 53 * MIME type of a document which is a directory that may contain additional 54 * documents. 55 * 56 * @see #buildContentsUri(Uri) 57 */ 58 public static final String MIME_TYPE_DIRECTORY = "vnd.android.cursor.dir/doc"; 59 60 /** {@hide} */ 61 public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER"; 62 63 /** 64 * {@link DocumentColumns#DOC_ID} value representing the root directory of a 65 * storage root. 66 */ 67 public static final String ROOT_DOC_ID = "0"; 68 69 /** 70 * Flag indicating that a document is a directory that supports creation of 71 * new files within it. 72 * 73 * @see DocumentColumns#FLAGS 74 * @see #buildContentsUri(Uri) 75 */ 76 public static final int FLAG_SUPPORTS_CREATE = 1; 77 78 /** 79 * Flag indicating that a document is renamable. 80 * 81 * @see DocumentColumns#FLAGS 82 * @see #renameDocument(ContentResolver, Uri, String) 83 */ 84 public static final int FLAG_SUPPORTS_RENAME = 1 << 1; 85 86 /** 87 * Flag indicating that a document is deletable. 88 * 89 * @see DocumentColumns#FLAGS 90 */ 91 public static final int FLAG_SUPPORTS_DELETE = 1 << 2; 92 93 /** 94 * Flag indicating that a document can be represented as a thumbnail. 95 * 96 * @see DocumentColumns#FLAGS 97 * @see #getThumbnail(ContentResolver, Uri, Point) 98 */ 99 public static final int FLAG_SUPPORTS_THUMBNAIL = 1 << 3; 100 101 /** 102 * Flag indicating that a document is a directory that supports search. 103 * 104 * @see DocumentColumns#FLAGS 105 */ 106 public static final int FLAG_SUPPORTS_SEARCH = 1 << 4; 107 108 /** 109 * Optimal dimensions for a document thumbnail request, stored as a 110 * {@link Point} object. This is only a hint, and the returned thumbnail may 111 * have different dimensions. 112 * 113 * @see ContentProvider#openTypedAssetFile(Uri, String, Bundle) 114 */ 115 public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; 116 117 /** 118 * Extra boolean flag included in a directory {@link Cursor#getExtras()} 119 * indicating that the backend can provide additional data if requested, 120 * such as additional search results. 121 */ 122 public static final String EXTRA_HAS_MORE = "has_more"; 123 124 /** 125 * Extra boolean flag included in a {@link Cursor#respond(Bundle)} call to a 126 * directory to request that additional data should be fetched. When 127 * requested data is ready, the provider should send a change notification 128 * to cause a requery. 129 * 130 * @see Cursor#respond(Bundle) 131 * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver, 132 * boolean) 133 */ 134 public static final String EXTRA_REQUEST_MORE = "request_more"; 135 136 private static final String PATH_ROOTS = "roots"; 137 private static final String PATH_DOCS = "docs"; 138 private static final String PATH_CONTENTS = "contents"; 139 private static final String PATH_SEARCH = "search"; 140 141 public static final String PARAM_QUERY = "query"; 142 143 /** 144 * Build URI representing the roots in a storage backend. 145 */ 146 public static Uri buildRootsUri(String authority) { 147 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 148 .authority(authority).appendPath(PATH_ROOTS).build(); 149 } 150 151 public static Uri buildRootUri(String authority, String rootId) { 152 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 153 .authority(authority).appendPath(PATH_ROOTS).appendPath(rootId).build(); 154 } 155 156 /** 157 * Build URI representing the given {@link DocumentColumns#DOC_ID} in a 158 * storage root. 159 */ 160 public static Uri buildDocumentUri(String authority, String rootId, String docId) { 161 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 162 .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId) 163 .build(); 164 } 165 166 /** 167 * Build URI representing the contents of the given directory in a storage 168 * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}. 169 */ 170 public static Uri buildContentsUri(String authority, String rootId, String docId) { 171 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 172 .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId) 173 .appendPath(PATH_CONTENTS).build(); 174 } 175 176 /** 177 * Build URI representing a search for matching documents under a directory 178 * in a storage backend. 179 */ 180 public static Uri buildSearchUri(String authority, String rootId, String docId, String query) { 181 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 182 .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId) 183 .appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build(); 184 } 185 186 public static Uri buildDocumentUri(Uri relatedUri, String docId) { 187 return buildDocumentUri(relatedUri.getAuthority(), getRootId(relatedUri), docId); 188 } 189 190 public static Uri buildContentsUri(Uri relatedUri) { 191 return buildContentsUri( 192 relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri)); 193 } 194 195 public static Uri buildSearchUri(Uri relatedUri, String query) { 196 return buildSearchUri( 197 relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri), query); 198 } 199 200 public static String getRootId(Uri documentUri) { 201 final List<String> paths = documentUri.getPathSegments(); 202 if (!PATH_ROOTS.equals(paths.get(0))) { 203 throw new IllegalArgumentException(); 204 } 205 return paths.get(1); 206 } 207 208 public static String getDocId(Uri documentUri) { 209 final List<String> paths = documentUri.getPathSegments(); 210 if (!PATH_ROOTS.equals(paths.get(0))) { 211 throw new IllegalArgumentException(); 212 } 213 if (!PATH_DOCS.equals(paths.get(2))) { 214 throw new IllegalArgumentException(); 215 } 216 return paths.get(3); 217 } 218 219 public static String getSearchQuery(Uri documentUri) { 220 return documentUri.getQueryParameter(PARAM_QUERY); 221 } 222 223 /** 224 * These are standard columns for document URIs. Storage backend providers 225 * <em>must</em> support at least these columns when queried. 226 * 227 * @see Intent#ACTION_OPEN_DOCUMENT 228 * @see Intent#ACTION_CREATE_DOCUMENT 229 */ 230 public interface DocumentColumns extends OpenableColumns { 231 /** 232 * The ID for a document under a storage backend root. Values 233 * <em>must</em> never change once returned. This field is read-only to 234 * document clients. 235 * <p> 236 * Type: STRING 237 */ 238 public static final String DOC_ID = "doc_id"; 239 240 /** 241 * MIME type of a document, matching the value returned by 242 * {@link ContentResolver#getType(android.net.Uri)}. This field must be 243 * provided when a new document is created, but after that the field is 244 * read-only. 245 * <p> 246 * Type: STRING 247 * 248 * @see DocumentsContract#MIME_TYPE_DIRECTORY 249 */ 250 public static final String MIME_TYPE = "mime_type"; 251 252 /** 253 * Timestamp when a document was last modified, in milliseconds since 254 * January 1, 1970 00:00:00.0 UTC. This field is read-only to document 255 * clients. 256 * <p> 257 * Type: INTEGER (long) 258 * 259 * @see System#currentTimeMillis() 260 */ 261 public static final String LAST_MODIFIED = "last_modified"; 262 263 /** 264 * Flags that apply to a specific document. This field is read-only to 265 * document clients. 266 * <p> 267 * Type: INTEGER (int) 268 */ 269 public static final String FLAGS = "flags"; 270 271 /** 272 * Summary for this document, or {@code null} to omit. 273 * <p> 274 * Type: STRING 275 */ 276 public static final String SUMMARY = "summary"; 277 } 278 279 /** 280 * Root that represents a cloud-based storage service. 281 * 282 * @see RootColumns#ROOT_TYPE 283 */ 284 public static final int ROOT_TYPE_SERVICE = 1; 285 286 /** 287 * Root that represents a shortcut to content that may be available 288 * elsewhere through another storage root. 289 * 290 * @see RootColumns#ROOT_TYPE 291 */ 292 public static final int ROOT_TYPE_SHORTCUT = 2; 293 294 /** 295 * Root that represents a physical storage device. 296 * 297 * @see RootColumns#ROOT_TYPE 298 */ 299 public static final int ROOT_TYPE_DEVICE = 3; 300 301 /** 302 * Root that represents a physical storage device that should only be 303 * displayed to advanced users. 304 * 305 * @see RootColumns#ROOT_TYPE 306 */ 307 public static final int ROOT_TYPE_DEVICE_ADVANCED = 4; 308 309 /** 310 * These are standard columns for the roots URI. 311 * 312 * @see DocumentsContract#buildRootsUri(String) 313 */ 314 public interface RootColumns { 315 public static final String ROOT_ID = "root_id"; 316 317 /** 318 * Storage root type, use for clustering. 319 * <p> 320 * Type: INTEGER (int) 321 * 322 * @see DocumentsContract#ROOT_TYPE_SERVICE 323 * @see DocumentsContract#ROOT_TYPE_DEVICE 324 */ 325 public static final String ROOT_TYPE = "root_type"; 326 327 /** 328 * Icon resource ID for this storage root, or {@code 0} to use the 329 * default {@link ProviderInfo#icon}. 330 * <p> 331 * Type: INTEGER (int) 332 */ 333 public static final String ICON = "icon"; 334 335 /** 336 * Title for this storage root, or {@code null} to use the default 337 * {@link ProviderInfo#labelRes}. 338 * <p> 339 * Type: STRING 340 */ 341 public static final String TITLE = "title"; 342 343 /** 344 * Summary for this storage root, or {@code null} to omit. 345 * <p> 346 * Type: STRING 347 */ 348 public static final String SUMMARY = "summary"; 349 350 /** 351 * Number of free bytes of available in this storage root, or -1 if 352 * unknown or unbounded. 353 * <p> 354 * Type: INTEGER (long) 355 */ 356 public static final String AVAILABLE_BYTES = "available_bytes"; 357 } 358 359 /** 360 * Return thumbnail representing the document at the given URI. Callers are 361 * responsible for their own caching. Given document must have 362 * {@link #FLAG_SUPPORTS_THUMBNAIL} set. 363 * 364 * @return decoded thumbnail, or {@code null} if problem was encountered. 365 */ 366 public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) { 367 final Bundle opts = new Bundle(); 368 opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size); 369 370 InputStream is = null; 371 try { 372 is = new AssetFileDescriptor.AutoCloseInputStream( 373 resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts)); 374 return BitmapFactory.decodeStream(is); 375 } catch (IOException e) { 376 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); 377 return null; 378 } finally { 379 IoUtils.closeQuietly(is); 380 } 381 } 382 383 /** 384 * Rename the document at the given URI. Given document must have 385 * {@link #FLAG_SUPPORTS_RENAME} set. 386 * 387 * @return if rename was successful. 388 */ 389 public static boolean renameDocument( 390 ContentResolver resolver, Uri documentUri, String displayName) { 391 final ContentValues values = new ContentValues(); 392 values.put(DocumentColumns.DISPLAY_NAME, displayName); 393 return (resolver.update(documentUri, values, null, null) == 1); 394 } 395} 396