DocumentsContract.java revision 251097b3789632000ccdaf7fb7d66a82ff37d882
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 * Type of root that represents a storage service, such as a cloud-based 341 * service. 342 * 343 * @see #COLUMN_ROOT_TYPE 344 */ 345 public static final int ROOT_TYPE_SERVICE = 1; 346 347 /** 348 * Type of root that represents a shortcut to content that may be 349 * available elsewhere through another storage root. 350 * 351 * @see #COLUMN_ROOT_TYPE 352 */ 353 public static final int ROOT_TYPE_SHORTCUT = 2; 354 355 /** 356 * Type of root that represents a physical storage device. 357 * 358 * @see #COLUMN_ROOT_TYPE 359 */ 360 public static final int ROOT_TYPE_DEVICE = 3; 361 362 /** 363 * Flag indicating that at least one directory under this root supports 364 * creating content. Roots with this flag will be shown when an 365 * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}. 366 * 367 * @see #COLUMN_FLAGS 368 */ 369 public static final int FLAG_SUPPORTS_CREATE = 1; 370 371 /** 372 * Flag indicating that this root offers content that is strictly local 373 * on the device. That is, no network requests are made for the content. 374 * 375 * @see #COLUMN_FLAGS 376 * @see Intent#EXTRA_LOCAL_ONLY 377 */ 378 public static final int FLAG_LOCAL_ONLY = 1 << 1; 379 380 /** 381 * Flag indicating that this root should only be visible to advanced 382 * users. 383 * 384 * @see #COLUMN_FLAGS 385 */ 386 public static final int FLAG_ADVANCED = 1 << 2; 387 388 /** 389 * Flag indicating that a root offers audio documents. When a user is 390 * selecting audio, roots not providing audio may be excluded. 391 * 392 * @see #COLUMN_FLAGS 393 * @see Intent#EXTRA_MIME_TYPES 394 */ 395 public static final int FLAG_PROVIDES_AUDIO = 1 << 3; 396 397 /** 398 * Flag indicating that a root offers video documents. When a user is 399 * selecting video, roots not providing video may be excluded. 400 * 401 * @see #COLUMN_FLAGS 402 * @see Intent#EXTRA_MIME_TYPES 403 */ 404 public static final int FLAG_PROVIDES_VIDEO = 1 << 4; 405 406 /** 407 * Flag indicating that a root offers image documents. When a user is 408 * selecting images, roots not providing images may be excluded. 409 * 410 * @see #COLUMN_FLAGS 411 * @see Intent#EXTRA_MIME_TYPES 412 */ 413 public static final int FLAG_PROVIDES_IMAGES = 1 << 5; 414 415 /** 416 * Flag indicating that this root can report recently modified 417 * documents. 418 * 419 * @see #COLUMN_FLAGS 420 * @see DocumentsContract#buildRecentDocumentsUri(String, String) 421 */ 422 public static final int FLAG_SUPPORTS_RECENTS = 1 << 6; 423 } 424 425 /** 426 * Optional boolean flag included in a directory {@link Cursor#getExtras()} 427 * indicating that a document provider is still loading data. For example, a 428 * provider has returned some results, but is still waiting on an 429 * outstanding network request. The provider must send a content changed 430 * notification when loading is finished. 431 * 432 * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver, 433 * boolean) 434 */ 435 public static final String EXTRA_LOADING = "loading"; 436 437 /** 438 * Optional string included in a directory {@link Cursor#getExtras()} 439 * providing an informational message that should be shown to a user. For 440 * example, a provider may wish to indicate that not all documents are 441 * available. 442 */ 443 public static final String EXTRA_INFO = "info"; 444 445 /** 446 * Optional string included in a directory {@link Cursor#getExtras()} 447 * providing an error message that should be shown to a user. For example, a 448 * provider may wish to indicate that a network error occurred. The user may 449 * choose to retry, resulting in a new query. 450 */ 451 public static final String EXTRA_ERROR = "error"; 452 453 /** {@hide} */ 454 public static final String METHOD_CREATE_DOCUMENT = "android:createDocument"; 455 /** {@hide} */ 456 public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument"; 457 458 /** {@hide} */ 459 public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; 460 461 private static final String PATH_ROOT = "root"; 462 private static final String PATH_RECENT = "recent"; 463 private static final String PATH_DOCUMENT = "document"; 464 private static final String PATH_CHILDREN = "children"; 465 private static final String PATH_SEARCH = "search"; 466 467 private static final String PARAM_QUERY = "query"; 468 469 /** 470 * Build Uri representing the roots of a document provider. When queried, a 471 * provider will return one or more rows with columns defined by 472 * {@link Root}. 473 * 474 * @see DocumentsProvider#queryRoots(String[]) 475 */ 476 public static Uri buildRootsUri(String authority) { 477 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 478 .authority(authority).appendPath(PATH_ROOT).build(); 479 } 480 481 /** 482 * Build Uri representing the recently modified documents of a specific 483 * root. When queried, a provider will return zero or more rows with columns 484 * defined by {@link Document}. 485 * 486 * @see DocumentsProvider#queryRecentDocuments(String, String[]) 487 * @see #getRootId(Uri) 488 */ 489 public static Uri buildRecentDocumentsUri(String authority, String rootId) { 490 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 491 .authority(authority).appendPath(PATH_ROOT).appendPath(rootId) 492 .appendPath(PATH_RECENT).build(); 493 } 494 495 /** 496 * Build Uri representing the given {@link Document#COLUMN_DOCUMENT_ID} in a 497 * document provider. When queried, a provider will return a single row with 498 * columns defined by {@link Document}. 499 * 500 * @see DocumentsProvider#queryDocument(String, String[]) 501 * @see #getDocumentId(Uri) 502 */ 503 public static Uri buildDocumentUri(String authority, String documentId) { 504 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 505 .authority(authority).appendPath(PATH_DOCUMENT).appendPath(documentId).build(); 506 } 507 508 /** 509 * Build Uri representing the children of the given directory in a document 510 * provider. When queried, a provider will return zero or more rows with 511 * columns defined by {@link Document}. 512 * 513 * @param parentDocumentId the document to return children for, which must 514 * be a directory with MIME type of 515 * {@link Document#MIME_TYPE_DIR}. 516 * @see DocumentsProvider#queryChildDocuments(String, String[], String) 517 * @see #getDocumentId(Uri) 518 */ 519 public static Uri buildChildDocumentsUri(String authority, String parentDocumentId) { 520 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 521 .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_CHILDREN) 522 .build(); 523 } 524 525 /** 526 * Build Uri representing a search for matching documents under a specific 527 * directory in a document provider. When queried, a provider will return 528 * zero or more rows with columns defined by {@link Document}. 529 * 530 * @param parentDocumentId the document to return children for, which must 531 * be both a directory with MIME type of 532 * {@link Document#MIME_TYPE_DIR} and have 533 * {@link Document#FLAG_DIR_SUPPORTS_SEARCH} set. 534 * @see DocumentsProvider#querySearchDocuments(String, String, String[]) 535 * @see #getDocumentId(Uri) 536 * @see #getSearchDocumentsQuery(Uri) 537 */ 538 public static Uri buildSearchDocumentsUri( 539 String authority, String parentDocumentId, String query) { 540 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) 541 .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_SEARCH) 542 .appendQueryParameter(PARAM_QUERY, query).build(); 543 } 544 545 /** 546 * Extract the {@link Root#COLUMN_ROOT_ID} from the given Uri. 547 */ 548 public static String getRootId(Uri rootUri) { 549 final List<String> paths = rootUri.getPathSegments(); 550 if (paths.size() < 2) { 551 throw new IllegalArgumentException("Not a root: " + rootUri); 552 } 553 if (!PATH_ROOT.equals(paths.get(0))) { 554 throw new IllegalArgumentException("Not a root: " + rootUri); 555 } 556 return paths.get(1); 557 } 558 559 /** 560 * Extract the {@link Document#COLUMN_DOCUMENT_ID} from the given Uri. 561 */ 562 public static String getDocumentId(Uri documentUri) { 563 final List<String> paths = documentUri.getPathSegments(); 564 if (paths.size() < 2) { 565 throw new IllegalArgumentException("Not a document: " + documentUri); 566 } 567 if (!PATH_DOCUMENT.equals(paths.get(0))) { 568 throw new IllegalArgumentException("Not a document: " + documentUri); 569 } 570 return paths.get(1); 571 } 572 573 /** 574 * Extract the search query from a Uri built by 575 * {@link #buildSearchDocumentsUri(String, String, String)}. 576 */ 577 public static String getSearchDocumentsQuery(Uri searchDocumentsUri) { 578 return searchDocumentsUri.getQueryParameter(PARAM_QUERY); 579 } 580 581 /** 582 * Return list of all documents that the calling package has "open." These 583 * are Uris matching {@link DocumentsContract} to which persistent 584 * read/write access has been granted, usually through 585 * {@link Intent#ACTION_OPEN_DOCUMENT} or 586 * {@link Intent#ACTION_CREATE_DOCUMENT}. 587 * 588 * @see Context#grantUriPermission(String, Uri, int) 589 * @see Context#revokeUriPermission(Uri, int) 590 * @see ContentResolver#getIncomingUriPermissionGrants(int, int) 591 */ 592 public static Uri[] getOpenDocuments(Context context) { 593 final int openedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION 594 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION; 595 final Uri[] uris = context.getContentResolver() 596 .getIncomingUriPermissionGrants(openedFlags, openedFlags); 597 598 // Filter to only include document providers 599 final PackageManager pm = context.getPackageManager(); 600 final List<Uri> result = Lists.newArrayList(); 601 for (Uri uri : uris) { 602 final ProviderInfo info = pm.resolveContentProvider( 603 uri.getAuthority(), PackageManager.GET_META_DATA); 604 if (info.metaData.containsKey(META_DATA_DOCUMENT_PROVIDER)) { 605 result.add(uri); 606 } 607 } 608 609 return result.toArray(new Uri[result.size()]); 610 } 611 612 /** 613 * Return thumbnail representing the document at the given Uri. Callers are 614 * responsible for their own in-memory caching. 615 * 616 * @param documentUri document to return thumbnail for, which must have 617 * {@link Document#FLAG_SUPPORTS_THUMBNAIL} set. 618 * @param size optimal thumbnail size desired. A provider may return a 619 * thumbnail of a different size, but never more than double the 620 * requested size. 621 * @param signal signal used to indicate that caller is no longer interested 622 * in the thumbnail. 623 * @return decoded thumbnail, or {@code null} if problem was encountered. 624 * @see DocumentsProvider#openDocumentThumbnail(String, Point, 625 * android.os.CancellationSignal) 626 */ 627 public static Bitmap getDocumentThumbnail( 628 ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal) { 629 final Bundle openOpts = new Bundle(); 630 openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size); 631 632 AssetFileDescriptor afd = null; 633 try { 634 afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal); 635 636 final FileDescriptor fd = afd.getFileDescriptor(); 637 final long offset = afd.getStartOffset(); 638 final long length = afd.getDeclaredLength(); 639 640 // Some thumbnails might be a region inside a larger file, such as 641 // an EXIF thumbnail. Since BitmapFactory aggressively seeks around 642 // the entire file, we read the region manually. 643 byte[] region = null; 644 if (offset > 0 && length <= 64 * KB_IN_BYTES) { 645 region = new byte[(int) length]; 646 Libcore.os.lseek(fd, offset, SEEK_SET); 647 if (IoBridge.read(fd, region, 0, region.length) != region.length) { 648 region = null; 649 } 650 } 651 652 // We requested a rough thumbnail size, but the remote size may have 653 // returned something giant, so defensively scale down as needed. 654 final BitmapFactory.Options opts = new BitmapFactory.Options(); 655 opts.inJustDecodeBounds = true; 656 if (region != null) { 657 BitmapFactory.decodeByteArray(region, 0, region.length, opts); 658 } else { 659 BitmapFactory.decodeFileDescriptor(fd, null, opts); 660 } 661 662 final int widthSample = opts.outWidth / size.x; 663 final int heightSample = opts.outHeight / size.y; 664 665 opts.inJustDecodeBounds = false; 666 opts.inSampleSize = Math.min(widthSample, heightSample); 667 Log.d(TAG, "Decoding with sample size " + opts.inSampleSize); 668 if (region != null) { 669 return BitmapFactory.decodeByteArray(region, 0, region.length, opts); 670 } else { 671 return BitmapFactory.decodeFileDescriptor(fd, null, opts); 672 } 673 } catch (ErrnoException e) { 674 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); 675 return null; 676 } catch (IOException e) { 677 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); 678 return null; 679 } finally { 680 IoUtils.closeQuietly(afd); 681 } 682 } 683 684 /** 685 * Create a new document with given MIME type and display name. 686 * 687 * @param parentDocumentUri directory with 688 * {@link Document#FLAG_DIR_SUPPORTS_CREATE} 689 * @param mimeType MIME type of new document 690 * @param displayName name of new document 691 * @return newly created document, or {@code null} if failed 692 */ 693 public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri, 694 String mimeType, String displayName) { 695 final Bundle in = new Bundle(); 696 in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(parentDocumentUri)); 697 in.putString(Document.COLUMN_MIME_TYPE, mimeType); 698 in.putString(Document.COLUMN_DISPLAY_NAME, displayName); 699 700 try { 701 final Bundle out = resolver.call(parentDocumentUri, METHOD_CREATE_DOCUMENT, null, in); 702 return buildDocumentUri( 703 parentDocumentUri.getAuthority(), out.getString(Document.COLUMN_DOCUMENT_ID)); 704 } catch (Exception e) { 705 Log.w(TAG, "Failed to create document", e); 706 return null; 707 } 708 } 709 710 /** 711 * Delete the given document. 712 * 713 * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE} 714 */ 715 public static boolean deleteDocument(ContentResolver resolver, Uri documentUri) { 716 final Bundle in = new Bundle(); 717 in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(documentUri)); 718 719 try { 720 final Bundle out = resolver.call(documentUri, METHOD_DELETE_DOCUMENT, null, in); 721 return true; 722 } catch (Exception e) { 723 Log.w(TAG, "Failed to delete document", e); 724 return false; 725 } 726 } 727} 728