DocumentsContract.java revision 9fb567b59112f99e64e0bff6f343188331bad28d
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 // TODO: flag indicating that document is writable? 109 110 /** 111 * Optimal dimensions for a document thumbnail request, stored as a 112 * {@link Point} object. This is only a hint, and the returned thumbnail may 113 * have different dimensions. 114 * 115 * @see ContentProvider#openTypedAssetFile(Uri, String, Bundle) 116 */ 117 public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; 118 119 /** 120 * Extra boolean flag included in a directory {@link Cursor#getExtras()} 121 * indicating that the backend can provide additional data if requested, 122 * such as additional search results. 123 */ 124 public static final String EXTRA_HAS_MORE = "has_more"; 125 126 /** 127 * Extra boolean flag included in a {@link Cursor#respond(Bundle)} call to a 128 * directory to request that additional data should be fetched. When 129 * requested data is ready, the provider should send a change notification 130 * to cause a requery. 131 * 132 * @see Cursor#respond(Bundle) 133 * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver, 134 * boolean) 135 */ 136 public static final String EXTRA_REQUEST_MORE = "request_more"; 137 138 private static final String PATH_ROOTS = "roots"; 139 private static final String PATH_DOCS = "docs"; 140 private static final String PATH_CONTENTS = "contents"; 141 private static final String PATH_SEARCH = "search"; 142 143 public static final String PARAM_QUERY = "query"; 144 145 /** 146 * Build URI representing the roots in a storage backend. 147 */ 148 public static Uri buildRootsUri(String authority) { 149 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 150 .authority(authority).appendPath(PATH_ROOTS).build(); 151 } 152 153 public static Uri buildRootUri(String authority, String rootId) { 154 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 155 .authority(authority).appendPath(PATH_ROOTS).appendPath(rootId).build(); 156 } 157 158 /** 159 * Build URI representing the given {@link DocumentColumns#DOC_ID} in a 160 * storage root. 161 */ 162 public static Uri buildDocumentUri(String authority, String rootId, String docId) { 163 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 164 .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId) 165 .build(); 166 } 167 168 /** 169 * Build URI representing the contents of the given directory in a storage 170 * backend. The given document must be {@link #MIME_TYPE_DIRECTORY}. 171 */ 172 public static Uri buildContentsUri(String authority, String rootId, String docId) { 173 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 174 .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId) 175 .appendPath(PATH_CONTENTS).build(); 176 } 177 178 /** 179 * Build URI representing a search for matching documents under a directory 180 * in a storage backend. 181 */ 182 public static Uri buildSearchUri(String authority, String rootId, String docId, String query) { 183 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 184 .appendPath(PATH_ROOTS).appendPath(rootId).appendPath(PATH_DOCS).appendPath(docId) 185 .appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build(); 186 } 187 188 public static Uri buildDocumentUri(Uri relatedUri, String docId) { 189 return buildDocumentUri(relatedUri.getAuthority(), getRootId(relatedUri), docId); 190 } 191 192 public static Uri buildContentsUri(Uri relatedUri) { 193 return buildContentsUri( 194 relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri)); 195 } 196 197 public static Uri buildSearchUri(Uri relatedUri, String query) { 198 return buildSearchUri( 199 relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri), query); 200 } 201 202 public static String getRootId(Uri documentUri) { 203 final List<String> paths = documentUri.getPathSegments(); 204 if (!PATH_ROOTS.equals(paths.get(0))) { 205 throw new IllegalArgumentException(); 206 } 207 return paths.get(1); 208 } 209 210 public static String getDocId(Uri documentUri) { 211 final List<String> paths = documentUri.getPathSegments(); 212 if (!PATH_ROOTS.equals(paths.get(0))) { 213 throw new IllegalArgumentException(); 214 } 215 if (!PATH_DOCS.equals(paths.get(2))) { 216 throw new IllegalArgumentException(); 217 } 218 return paths.get(3); 219 } 220 221 public static String getSearchQuery(Uri documentUri) { 222 return documentUri.getQueryParameter(PARAM_QUERY); 223 } 224 225 /** 226 * These are standard columns for document URIs. Storage backend providers 227 * <em>must</em> support at least these columns when queried. 228 * 229 * @see Intent#ACTION_OPEN_DOCUMENT 230 * @see Intent#ACTION_CREATE_DOCUMENT 231 */ 232 public interface DocumentColumns extends OpenableColumns { 233 /** 234 * The ID for a document under a storage backend root. Values 235 * <em>must</em> never change once returned. This field is read-only to 236 * document clients. 237 * <p> 238 * Type: STRING 239 */ 240 public static final String DOC_ID = "doc_id"; 241 242 /** 243 * MIME type of a document, matching the value returned by 244 * {@link ContentResolver#getType(android.net.Uri)}. This field must be 245 * provided when a new document is created, but after that the field is 246 * read-only. 247 * <p> 248 * Type: STRING 249 * 250 * @see DocumentsContract#MIME_TYPE_DIRECTORY 251 */ 252 public static final String MIME_TYPE = "mime_type"; 253 254 /** 255 * Timestamp when a document was last modified, in milliseconds since 256 * January 1, 1970 00:00:00.0 UTC. This field is read-only to document 257 * clients. 258 * <p> 259 * Type: INTEGER (long) 260 * 261 * @see System#currentTimeMillis() 262 */ 263 public static final String LAST_MODIFIED = "last_modified"; 264 265 /** 266 * Flags that apply to a specific document. This field is read-only to 267 * document clients. 268 * <p> 269 * Type: INTEGER (int) 270 */ 271 public static final String FLAGS = "flags"; 272 273 /** 274 * Summary for this document, or {@code null} to omit. 275 * <p> 276 * Type: STRING 277 */ 278 public static final String SUMMARY = "summary"; 279 } 280 281 /** 282 * Root that represents a cloud-based storage service. 283 * 284 * @see RootColumns#ROOT_TYPE 285 */ 286 public static final int ROOT_TYPE_SERVICE = 1; 287 288 /** 289 * Root that represents a shortcut to content that may be available 290 * elsewhere through another storage root. 291 * 292 * @see RootColumns#ROOT_TYPE 293 */ 294 public static final int ROOT_TYPE_SHORTCUT = 2; 295 296 /** 297 * Root that represents a physical storage device. 298 * 299 * @see RootColumns#ROOT_TYPE 300 */ 301 public static final int ROOT_TYPE_DEVICE = 3; 302 303 /** 304 * Root that represents a physical storage device that should only be 305 * displayed to advanced users. 306 * 307 * @see RootColumns#ROOT_TYPE 308 */ 309 public static final int ROOT_TYPE_DEVICE_ADVANCED = 4; 310 311 /** 312 * These are standard columns for the roots URI. 313 * 314 * @see DocumentsContract#buildRootsUri(String) 315 */ 316 public interface RootColumns { 317 public static final String ROOT_ID = "root_id"; 318 319 /** 320 * Storage root type, use for clustering. 321 * <p> 322 * Type: INTEGER (int) 323 * 324 * @see DocumentsContract#ROOT_TYPE_SERVICE 325 * @see DocumentsContract#ROOT_TYPE_DEVICE 326 */ 327 public static final String ROOT_TYPE = "root_type"; 328 329 /** 330 * Icon resource ID for this storage root, or {@code 0} to use the 331 * default {@link ProviderInfo#icon}. 332 * <p> 333 * Type: INTEGER (int) 334 */ 335 public static final String ICON = "icon"; 336 337 /** 338 * Title for this storage root, or {@code null} to use the default 339 * {@link ProviderInfo#labelRes}. 340 * <p> 341 * Type: STRING 342 */ 343 public static final String TITLE = "title"; 344 345 /** 346 * Summary for this storage root, or {@code null} to omit. 347 * <p> 348 * Type: STRING 349 */ 350 public static final String SUMMARY = "summary"; 351 352 /** 353 * Number of free bytes of available in this storage root, or -1 if 354 * unknown or unbounded. 355 * <p> 356 * Type: INTEGER (long) 357 */ 358 public static final String AVAILABLE_BYTES = "available_bytes"; 359 } 360 361 /** 362 * Return thumbnail representing the document at the given URI. Callers are 363 * responsible for their own caching. Given document must have 364 * {@link #FLAG_SUPPORTS_THUMBNAIL} set. 365 * 366 * @return decoded thumbnail, or {@code null} if problem was encountered. 367 */ 368 public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) { 369 final Bundle opts = new Bundle(); 370 opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size); 371 372 InputStream is = null; 373 try { 374 is = new AssetFileDescriptor.AutoCloseInputStream( 375 resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts)); 376 return BitmapFactory.decodeStream(is); 377 } catch (IOException e) { 378 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); 379 return null; 380 } finally { 381 IoUtils.closeQuietly(is); 382 } 383 } 384 385 /** 386 * Rename the document at the given URI. Given document must have 387 * {@link #FLAG_SUPPORTS_RENAME} set. 388 * 389 * @return if rename was successful. 390 */ 391 public static boolean renameDocument( 392 ContentResolver resolver, Uri documentUri, String displayName) { 393 final ContentValues values = new ContentValues(); 394 values.put(DocumentColumns.DISPLAY_NAME, displayName); 395 return (resolver.update(documentUri, values, null, null) == 1); 396 } 397} 398