DocumentsContract.java revision 923396b7720367ec99c889725fddfa556e703971
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 static android.net.TrafficStats.KB_IN_BYTES; 20import static libcore.io.OsConstants.SEEK_SET; 21 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.PackageManager; 26import android.content.pm.ProviderInfo; 27import android.content.res.AssetFileDescriptor; 28import android.database.Cursor; 29import android.graphics.Bitmap; 30import android.graphics.BitmapFactory; 31import android.graphics.Point; 32import android.net.Uri; 33import android.os.Bundle; 34import android.os.CancellationSignal; 35import android.os.ParcelFileDescriptor; 36import android.os.ParcelFileDescriptor.OnCloseListener; 37import android.util.Log; 38 39import com.google.android.collect.Lists; 40 41import libcore.io.ErrnoException; 42import libcore.io.IoBridge; 43import libcore.io.IoUtils; 44import libcore.io.Libcore; 45 46import java.io.FileDescriptor; 47import java.io.IOException; 48import java.util.List; 49 50/** 51 * Defines the contract between a documents provider and the platform. 52 * <p> 53 * To create a document provider, extend {@link DocumentsProvider}, which 54 * provides a foundational implementation of this contract. 55 * 56 * @see DocumentsProvider 57 */ 58public final class DocumentsContract { 59 private static final String TAG = "Documents"; 60 61 // content://com.example/root/ 62 // content://com.example/root/sdcard/ 63 // content://com.example/root/sdcard/recent/ 64 // content://com.example/document/12/ 65 // content://com.example/document/12/children/ 66 // content://com.example/document/12/search/?query=pony 67 68 private DocumentsContract() { 69 } 70 71 /** {@hide} */ 72 public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER"; 73 74 /** {@hide} */ 75 public static final String ACTION_MANAGE_DOCUMENTS = "android.provider.action.MANAGE_DOCUMENTS"; 76 77 /** 78 * Constants related to a document, including {@link Cursor} columns names 79 * and flags. 80 * <p> 81 * A document can be either an openable file (with a specific MIME type), or 82 * a directory containing additional documents (with the 83 * {@link #MIME_TYPE_DIR} MIME type). 84 * <p> 85 * All columns are <em>read-only</em> to client applications. 86 */ 87 public final static class Document { 88 private Document() { 89 } 90 91 /** 92 * Unique ID of a document. This ID is both provided by and interpreted 93 * by a {@link DocumentsProvider}, and should be treated as an opaque 94 * value by client applications. 95 * <p> 96 * Each document must have a unique ID within a provider, but that 97 * single document may be included as a child of multiple directories. 98 * <p> 99 * A provider must always return durable IDs, since they will be used to 100 * issue long-term Uri permission grants when an application interacts 101 * with {@link Intent#ACTION_OPEN_DOCUMENT} and 102 * {@link Intent#ACTION_CREATE_DOCUMENT}. 103 * <p> 104 * Type: STRING 105 */ 106 public static final String COLUMN_DOCUMENT_ID = "document_id"; 107 108 /** 109 * Concrete MIME type of a document. For example, "image/png" or 110 * "application/pdf" for openable files. A document can also be a 111 * directory containing additional documents, which is represented with 112 * the {@link #MIME_TYPE_DIR} MIME type. 113 * <p> 114 * Type: STRING 115 * 116 * @see #MIME_TYPE_DIR 117 */ 118 public static final String COLUMN_MIME_TYPE = "mime_type"; 119 120 /** 121 * Display name of a document, used as the primary title displayed to a 122 * user. 123 * <p> 124 * Type: STRING 125 */ 126 public static final String COLUMN_DISPLAY_NAME = OpenableColumns.DISPLAY_NAME; 127 128 /** 129 * Summary of a document, which may be shown to a user. The summary may 130 * be {@code null}. 131 * <p> 132 * Type: STRING 133 */ 134 public static final String COLUMN_SUMMARY = "summary"; 135 136 /** 137 * Timestamp when a document was last modified, in milliseconds since 138 * January 1, 1970 00:00:00.0 UTC, or {@code null} if unknown. A 139 * {@link DocumentsProvider} can update this field using events from 140 * {@link OnCloseListener} or other reliable 141 * {@link ParcelFileDescriptor} transports. 142 * <p> 143 * Type: INTEGER (long) 144 * 145 * @see System#currentTimeMillis() 146 */ 147 public static final String COLUMN_LAST_MODIFIED = "last_modified"; 148 149 /** 150 * Specific icon resource ID for a document, or {@code null} to use 151 * platform default icon based on {@link #COLUMN_MIME_TYPE}. 152 * <p> 153 * Type: INTEGER (int) 154 */ 155 public static final String COLUMN_ICON = "icon"; 156 157 /** 158 * Flags that apply to a document. 159 * <p> 160 * Type: INTEGER (int) 161 * 162 * @see #FLAG_SUPPORTS_WRITE 163 * @see #FLAG_SUPPORTS_DELETE 164 * @see #FLAG_SUPPORTS_THUMBNAIL 165 * @see #FLAG_DIR_PREFERS_GRID 166 * @see #FLAG_DIR_SUPPORTS_CREATE 167 * @see #FLAG_DIR_SUPPORTS_SEARCH 168 */ 169 public static final String COLUMN_FLAGS = "flags"; 170 171 /** 172 * Size of a document, in bytes, or {@code null} if unknown. 173 * <p> 174 * Type: INTEGER (long) 175 */ 176 public static final String COLUMN_SIZE = OpenableColumns.SIZE; 177 178 /** 179 * MIME type of a document which is a directory that may contain 180 * additional documents. 181 * 182 * @see #COLUMN_MIME_TYPE 183 */ 184 public static final String MIME_TYPE_DIR = "vnd.android.document/directory"; 185 186 /** 187 * Flag indicating that a document can be represented as a thumbnail. 188 * 189 * @see #COLUMN_FLAGS 190 * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri, 191 * Point, CancellationSignal) 192 * @see DocumentsProvider#openDocumentThumbnail(String, Point, 193 * android.os.CancellationSignal) 194 */ 195 public static final int FLAG_SUPPORTS_THUMBNAIL = 1; 196 197 /** 198 * Flag indicating that a document supports writing. 199 * <p> 200 * When a document is opened with {@link Intent#ACTION_OPEN_DOCUMENT}, 201 * the calling application is granted both 202 * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and 203 * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. However, the actual 204 * writability of a document may change over time, for example due to 205 * remote access changes. This flag indicates that a document client can 206 * expect {@link ContentResolver#openOutputStream(Uri)} to succeed. 207 * 208 * @see #COLUMN_FLAGS 209 */ 210 public static final int FLAG_SUPPORTS_WRITE = 1 << 1; 211 212 /** 213 * Flag indicating that a document is deletable. 214 * 215 * @see #COLUMN_FLAGS 216 * @see DocumentsContract#deleteDocument(ContentResolver, Uri) 217 * @see DocumentsProvider#deleteDocument(String) 218 */ 219 public static final int FLAG_SUPPORTS_DELETE = 1 << 2; 220 221 /** 222 * Flag indicating that a document is a directory that supports creation 223 * of new files within it. Only valid when {@link #COLUMN_MIME_TYPE} is 224 * {@link #MIME_TYPE_DIR}. 225 * 226 * @see #COLUMN_FLAGS 227 * @see DocumentsContract#createDocument(ContentResolver, Uri, String, 228 * String) 229 * @see DocumentsProvider#createDocument(String, String, String) 230 */ 231 public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3; 232 233 /** 234 * Flag indicating that a directory supports search. Only valid when 235 * {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}. 236 * 237 * @see #COLUMN_FLAGS 238 * @see DocumentsProvider#querySearchDocuments(String, String, 239 * String[]) 240 */ 241 public static final int FLAG_DIR_SUPPORTS_SEARCH = 1 << 4; 242 243 /** 244 * Flag indicating that a directory prefers its contents be shown in a 245 * larger format grid. Usually suitable when a directory contains mostly 246 * pictures. Only valid when {@link #COLUMN_MIME_TYPE} is 247 * {@link #MIME_TYPE_DIR}. 248 * 249 * @see #COLUMN_FLAGS 250 */ 251 public static final int FLAG_DIR_PREFERS_GRID = 1 << 5; 252 } 253 254 /** 255 * Constants related to a root of documents, including {@link Cursor} 256 * columns names and flags. 257 * <p> 258 * All columns are <em>read-only</em> to client applications. 259 */ 260 public final static class Root { 261 private Root() { 262 } 263 264 /** 265 * Unique ID of a root. This ID is both provided by and interpreted by a 266 * {@link DocumentsProvider}, and should be treated as an opaque value 267 * by client applications. 268 * <p> 269 * Type: STRING 270 */ 271 public static final String COLUMN_ROOT_ID = "root_id"; 272 273 /** 274 * Type of a root, used for clustering when presenting multiple roots to 275 * a user. 276 * <p> 277 * Type: INTEGER (int) 278 * 279 * @see #ROOT_TYPE_SERVICE 280 * @see #ROOT_TYPE_SHORTCUT 281 * @see #ROOT_TYPE_DEVICE 282 */ 283 public static final String COLUMN_ROOT_TYPE = "root_type"; 284 285 /** 286 * Flags that apply to a root. 287 * <p> 288 * Type: INTEGER (int) 289 * 290 * @see #FLAG_LOCAL_ONLY 291 * @see #FLAG_SUPPORTS_CREATE 292 * @see #FLAG_ADVANCED 293 * @see #FLAG_PROVIDES_AUDIO 294 * @see #FLAG_PROVIDES_IMAGES 295 * @see #FLAG_PROVIDES_VIDEO 296 */ 297 public static final String COLUMN_FLAGS = "flags"; 298 299 /** 300 * Icon resource ID for a root. 301 * <p> 302 * Type: INTEGER (int) 303 */ 304 public static final String COLUMN_ICON = "icon"; 305 306 /** 307 * Title for a root, which will be shown to a user. 308 * <p> 309 * Type: STRING 310 */ 311 public static final String COLUMN_TITLE = "title"; 312 313 /** 314 * Summary for this root, which may be shown to a user. The summary may 315 * be {@code null}. 316 * <p> 317 * Type: STRING 318 */ 319 public static final String COLUMN_SUMMARY = "summary"; 320 321 /** 322 * Document which is a directory that represents the top directory of 323 * this root. 324 * <p> 325 * Type: STRING 326 * 327 * @see Document#COLUMN_DOCUMENT_ID 328 */ 329 public static final String COLUMN_DOCUMENT_ID = "document_id"; 330 331 /** 332 * Number of bytes available in this root, or {@code null} if unknown or 333 * unbounded. 334 * <p> 335 * Type: INTEGER (long) 336 */ 337 public static final String COLUMN_AVAILABLE_BYTES = "available_bytes"; 338 339 /** 340 * MIME types supported by this root, or {@code null} if the root 341 * supports all MIME types. Multiple MIME types can be separated by a 342 * newline. For example, a root supporting audio might use 343 * "audio/*\napplication/x-flac". 344 * <p> 345 * Type: String 346 */ 347 public static final String COLUMN_MIME_TYPES = "mime_types"; 348 349 /** 350 * Type of root that represents a storage service, such as a cloud-based 351 * service. 352 * 353 * @see #COLUMN_ROOT_TYPE 354 */ 355 public static final int ROOT_TYPE_SERVICE = 1; 356 357 /** 358 * Type of root that represents a shortcut to content that may be 359 * available elsewhere through another storage root. 360 * 361 * @see #COLUMN_ROOT_TYPE 362 */ 363 public static final int ROOT_TYPE_SHORTCUT = 2; 364 365 /** 366 * Type of root that represents a physical storage device. 367 * 368 * @see #COLUMN_ROOT_TYPE 369 */ 370 public static final int ROOT_TYPE_DEVICE = 3; 371 372 /** 373 * Flag indicating that at least one directory under this root supports 374 * creating content. Roots with this flag will be shown when an 375 * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}. 376 * 377 * @see #COLUMN_FLAGS 378 */ 379 public static final int FLAG_SUPPORTS_CREATE = 1; 380 381 /** 382 * Flag indicating that this root offers content that is strictly local 383 * on the device. That is, no network requests are made for the content. 384 * 385 * @see #COLUMN_FLAGS 386 * @see Intent#EXTRA_LOCAL_ONLY 387 */ 388 public static final int FLAG_LOCAL_ONLY = 1 << 1; 389 390 /** 391 * Flag indicating that this root should only be visible to advanced 392 * users. 393 * 394 * @see #COLUMN_FLAGS 395 */ 396 public static final int FLAG_ADVANCED = 1 << 2; 397 398 /** 399 * Flag indicating that this root can report recently modified 400 * documents. 401 * 402 * @see #COLUMN_FLAGS 403 * @see DocumentsContract#buildRecentDocumentsUri(String, String) 404 */ 405 public static final int FLAG_SUPPORTS_RECENTS = 1 << 3; 406 } 407 408 /** 409 * Optional boolean flag included in a directory {@link Cursor#getExtras()} 410 * indicating that a document provider is still loading data. For example, a 411 * provider has returned some results, but is still waiting on an 412 * outstanding network request. The provider must send a content changed 413 * notification when loading is finished. 414 * 415 * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver, 416 * boolean) 417 */ 418 public static final String EXTRA_LOADING = "loading"; 419 420 /** 421 * Optional string included in a directory {@link Cursor#getExtras()} 422 * providing an informational message that should be shown to a user. For 423 * example, a provider may wish to indicate that not all documents are 424 * available. 425 */ 426 public static final String EXTRA_INFO = "info"; 427 428 /** 429 * Optional string included in a directory {@link Cursor#getExtras()} 430 * providing an error message that should be shown to a user. For example, a 431 * provider may wish to indicate that a network error occurred. The user may 432 * choose to retry, resulting in a new query. 433 */ 434 public static final String EXTRA_ERROR = "error"; 435 436 /** {@hide} */ 437 public static final String METHOD_CREATE_DOCUMENT = "android:createDocument"; 438 /** {@hide} */ 439 public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument"; 440 441 /** {@hide} */ 442 public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; 443 444 private static final String PATH_ROOT = "root"; 445 private static final String PATH_RECENT = "recent"; 446 private static final String PATH_DOCUMENT = "document"; 447 private static final String PATH_CHILDREN = "children"; 448 private static final String PATH_SEARCH = "search"; 449 450 private static final String PARAM_QUERY = "query"; 451 452 /** 453 * Build Uri representing the roots of a document provider. When queried, a 454 * provider will return one or more rows with columns defined by 455 * {@link Root}. 456 * 457 * @see DocumentsProvider#queryRoots(String[]) 458 */ 459 public static Uri buildRootsUri(String authority) { 460 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 461 .authority(authority).appendPath(PATH_ROOT).build(); 462 } 463 464 /** 465 * Build Uri representing the recently modified documents of a specific 466 * root. When queried, a provider will return zero or more rows with columns 467 * defined by {@link Document}. 468 * 469 * @see DocumentsProvider#queryRecentDocuments(String, String[]) 470 * @see #getRootId(Uri) 471 */ 472 public static Uri buildRecentDocumentsUri(String authority, String rootId) { 473 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 474 .authority(authority).appendPath(PATH_ROOT).appendPath(rootId) 475 .appendPath(PATH_RECENT).build(); 476 } 477 478 /** 479 * Build Uri representing the given {@link Document#COLUMN_DOCUMENT_ID} in a 480 * document provider. When queried, a provider will return a single row with 481 * columns defined by {@link Document}. 482 * 483 * @see DocumentsProvider#queryDocument(String, String[]) 484 * @see #getDocumentId(Uri) 485 */ 486 public static Uri buildDocumentUri(String authority, String documentId) { 487 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 488 .authority(authority).appendPath(PATH_DOCUMENT).appendPath(documentId).build(); 489 } 490 491 /** 492 * Build Uri representing the children of the given directory in a document 493 * provider. When queried, a provider will return zero or more rows with 494 * columns defined by {@link Document}. 495 * 496 * @param parentDocumentId the document to return children for, which must 497 * be a directory with MIME type of 498 * {@link Document#MIME_TYPE_DIR}. 499 * @see DocumentsProvider#queryChildDocuments(String, String[], String) 500 * @see #getDocumentId(Uri) 501 */ 502 public static Uri buildChildDocumentsUri(String authority, String parentDocumentId) { 503 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 504 .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_CHILDREN) 505 .build(); 506 } 507 508 /** 509 * Build Uri representing a search for matching documents under a specific 510 * directory in a document provider. When queried, a provider will return 511 * zero or more rows with columns defined by {@link Document}. 512 * 513 * @param parentDocumentId the document to return children for, which must 514 * be both a directory with MIME type of 515 * {@link Document#MIME_TYPE_DIR} and have 516 * {@link Document#FLAG_DIR_SUPPORTS_SEARCH} set. 517 * @see DocumentsProvider#querySearchDocuments(String, String, String[]) 518 * @see #getDocumentId(Uri) 519 * @see #getSearchDocumentsQuery(Uri) 520 */ 521 public static Uri buildSearchDocumentsUri( 522 String authority, String parentDocumentId, String query) { 523 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 524 .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_SEARCH) 525 .appendQueryParameter(PARAM_QUERY, query).build(); 526 } 527 528 /** 529 * Extract the {@link Root#COLUMN_ROOT_ID} from the given Uri. 530 */ 531 public static String getRootId(Uri rootUri) { 532 final List<String> paths = rootUri.getPathSegments(); 533 if (paths.size() < 2) { 534 throw new IllegalArgumentException("Not a root: " + rootUri); 535 } 536 if (!PATH_ROOT.equals(paths.get(0))) { 537 throw new IllegalArgumentException("Not a root: " + rootUri); 538 } 539 return paths.get(1); 540 } 541 542 /** 543 * Extract the {@link Document#COLUMN_DOCUMENT_ID} from the given Uri. 544 */ 545 public static String getDocumentId(Uri documentUri) { 546 final List<String> paths = documentUri.getPathSegments(); 547 if (paths.size() < 2) { 548 throw new IllegalArgumentException("Not a document: " + documentUri); 549 } 550 if (!PATH_DOCUMENT.equals(paths.get(0))) { 551 throw new IllegalArgumentException("Not a document: " + documentUri); 552 } 553 return paths.get(1); 554 } 555 556 /** 557 * Extract the search query from a Uri built by 558 * {@link #buildSearchDocumentsUri(String, String, String)}. 559 */ 560 public static String getSearchDocumentsQuery(Uri searchDocumentsUri) { 561 return searchDocumentsUri.getQueryParameter(PARAM_QUERY); 562 } 563 564 /** 565 * Return list of all documents that the calling package has "open." These 566 * are Uris matching {@link DocumentsContract} to which persistent 567 * read/write access has been granted, usually through 568 * {@link Intent#ACTION_OPEN_DOCUMENT} or 569 * {@link Intent#ACTION_CREATE_DOCUMENT}. 570 * 571 * @see Context#grantUriPermission(String, Uri, int) 572 * @see Context#revokeUriPermission(Uri, int) 573 * @see ContentResolver#getIncomingUriPermissionGrants(int, int) 574 */ 575 public static Uri[] getOpenDocuments(Context context) { 576 final int openedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION 577 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION; 578 final Uri[] uris = context.getContentResolver() 579 .getIncomingUriPermissionGrants(openedFlags, openedFlags); 580 581 // Filter to only include document providers 582 final PackageManager pm = context.getPackageManager(); 583 final List<Uri> result = Lists.newArrayList(); 584 for (Uri uri : uris) { 585 final ProviderInfo info = pm.resolveContentProvider( 586 uri.getAuthority(), PackageManager.GET_META_DATA); 587 if (info.metaData.containsKey(META_DATA_DOCUMENT_PROVIDER)) { 588 result.add(uri); 589 } 590 } 591 592 return result.toArray(new Uri[result.size()]); 593 } 594 595 /** 596 * Return thumbnail representing the document at the given Uri. Callers are 597 * responsible for their own in-memory caching. 598 * 599 * @param documentUri document to return thumbnail for, which must have 600 * {@link Document#FLAG_SUPPORTS_THUMBNAIL} set. 601 * @param size optimal thumbnail size desired. A provider may return a 602 * thumbnail of a different size, but never more than double the 603 * requested size. 604 * @param signal signal used to indicate that caller is no longer interested 605 * in the thumbnail. 606 * @return decoded thumbnail, or {@code null} if problem was encountered. 607 * @see DocumentsProvider#openDocumentThumbnail(String, Point, 608 * android.os.CancellationSignal) 609 */ 610 public static Bitmap getDocumentThumbnail( 611 ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal) { 612 final Bundle openOpts = new Bundle(); 613 openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size); 614 615 AssetFileDescriptor afd = null; 616 try { 617 afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal); 618 619 final FileDescriptor fd = afd.getFileDescriptor(); 620 final long offset = afd.getStartOffset(); 621 final long length = afd.getDeclaredLength(); 622 623 // Some thumbnails might be a region inside a larger file, such as 624 // an EXIF thumbnail. Since BitmapFactory aggressively seeks around 625 // the entire file, we read the region manually. 626 byte[] region = null; 627 if (offset > 0 && length <= 64 * KB_IN_BYTES) { 628 region = new byte[(int) length]; 629 Libcore.os.lseek(fd, offset, SEEK_SET); 630 if (IoBridge.read(fd, region, 0, region.length) != region.length) { 631 region = null; 632 } 633 } 634 635 // We requested a rough thumbnail size, but the remote size may have 636 // returned something giant, so defensively scale down as needed. 637 final BitmapFactory.Options opts = new BitmapFactory.Options(); 638 opts.inJustDecodeBounds = true; 639 if (region != null) { 640 BitmapFactory.decodeByteArray(region, 0, region.length, opts); 641 } else { 642 BitmapFactory.decodeFileDescriptor(fd, null, opts); 643 } 644 645 final int widthSample = opts.outWidth / size.x; 646 final int heightSample = opts.outHeight / size.y; 647 648 opts.inJustDecodeBounds = false; 649 opts.inSampleSize = Math.min(widthSample, heightSample); 650 Log.d(TAG, "Decoding with sample size " + opts.inSampleSize); 651 if (region != null) { 652 return BitmapFactory.decodeByteArray(region, 0, region.length, opts); 653 } else { 654 return BitmapFactory.decodeFileDescriptor(fd, null, opts); 655 } 656 } catch (ErrnoException e) { 657 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); 658 return null; 659 } catch (IOException e) { 660 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); 661 return null; 662 } finally { 663 IoUtils.closeQuietly(afd); 664 } 665 } 666 667 /** 668 * Create a new document with given MIME type and display name. 669 * 670 * @param parentDocumentUri directory with 671 * {@link Document#FLAG_DIR_SUPPORTS_CREATE} 672 * @param mimeType MIME type of new document 673 * @param displayName name of new document 674 * @return newly created document, or {@code null} if failed 675 */ 676 public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri, 677 String mimeType, String displayName) { 678 final Bundle in = new Bundle(); 679 in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(parentDocumentUri)); 680 in.putString(Document.COLUMN_MIME_TYPE, mimeType); 681 in.putString(Document.COLUMN_DISPLAY_NAME, displayName); 682 683 try { 684 final Bundle out = resolver.call(parentDocumentUri, METHOD_CREATE_DOCUMENT, null, in); 685 return buildDocumentUri( 686 parentDocumentUri.getAuthority(), out.getString(Document.COLUMN_DOCUMENT_ID)); 687 } catch (Exception e) { 688 Log.w(TAG, "Failed to create document", e); 689 return null; 690 } 691 } 692 693 /** 694 * Delete the given document. 695 * 696 * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE} 697 */ 698 public static boolean deleteDocument(ContentResolver resolver, Uri documentUri) { 699 final Bundle in = new Bundle(); 700 in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(documentUri)); 701 702 try { 703 final Bundle out = resolver.call(documentUri, METHOD_DELETE_DOCUMENT, null, in); 704 return true; 705 } catch (Exception e) { 706 Log.w(TAG, "Failed to delete document", e); 707 return false; 708 } 709 } 710} 711