19ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey/*
29ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * Copyright (C) 2013 The Android Open Source Project
39ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey *
49ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
59ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * you may not use this file except in compliance with the License.
69ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * You may obtain a copy of the License at
79ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey *
89ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
99ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey *
109ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * Unless required by applicable law or agreed to in writing, software
119ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
129ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * See the License for the specific language governing permissions and
149ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey * limitations under the License.
159ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey */
169ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
179ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeypackage android.provider;
189ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
196398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkeyimport static android.net.TrafficStats.KB_IN_BYTES;
2034385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport static android.system.OsConstants.SEEK_SET;
216398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey
22323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKayimport android.annotation.Nullable;
23de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.content.ContentProviderClient;
249ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.content.ContentResolver;
25ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkeyimport android.content.Context;
269ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.content.Intent;
27d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkeyimport android.content.pm.ResolveInfo;
289ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.content.res.AssetFileDescriptor;
2920d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkeyimport android.database.Cursor;
309ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.graphics.Bitmap;
319ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.graphics.BitmapFactory;
32c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkeyimport android.graphics.Matrix;
339ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.graphics.Point;
34c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkeyimport android.media.ExifInterface;
359ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.net.Uri;
369ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.os.Bundle;
37ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport android.os.CancellationSignal;
3833819318f61224cdda45e5e9a70dc9cdb9f33911Jeff Sharkeyimport android.os.OperationCanceledException;
39bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkeyimport android.os.ParcelFileDescriptor;
40bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkeyimport android.os.ParcelFileDescriptor.OnCloseListener;
41de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.os.RemoteException;
4204a5d40cf35fb2c2fca2c1bfd573e5916d804ef6Felipe Lemeimport android.os.storage.StorageVolume;
4334385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.ErrnoException;
4434385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os;
459ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport android.util.Log;
469ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
479ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport libcore.io.IoUtils;
489ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
49de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.BufferedInputStream;
50c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkeyimport java.io.File;
519d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport java.io.FileDescriptor;
52de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.FileInputStream;
53c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkeyimport java.io.FileNotFoundException;
549ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeyimport java.io.IOException;
55dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkeyimport java.util.List;
569ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
579ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey/**
58bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey * Defines the contract between a documents provider and the platform.
59bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey * <p>
60aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * To create a document provider, extend {@link DocumentsProvider}, which
61aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * provides a foundational implementation of this contract.
6221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * <p>
6321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * All client apps must hold a valid URI permission grant to access documents,
6421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey * typically issued when a user makes a selection through
65b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT},
6604a5d40cf35fb2c2fca2c1bfd573e5916d804ef6Felipe Leme * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, or
6704a5d40cf35fb2c2fca2c1bfd573e5916d804ef6Felipe Leme * {@link StorageVolume#createAccessIntent(String) StorageVolume.createAccessIntent}.
68bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey *
69aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey * @see DocumentsProvider
709ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey */
719ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkeypublic final class DocumentsContract {
72d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    private static final String TAG = "DocumentsContract";
739ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
74ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    // content://com.example/root/
75ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    // content://com.example/root/sdcard/
76ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    // content://com.example/root/sdcard/recent/
773e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey    // content://com.example/root/sdcard/search/?query=pony
78ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    // content://com.example/document/12/
79ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    // content://com.example/document/12/children/
80b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    // content://com.example/tree/12/document/24/
81b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    // content://com.example/tree/12/document/24/children/
82aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
83aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    private DocumentsContract() {
84aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey    }
859ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
8685f5f8132015d8a5043ea4413702420d0d157c9fJeff Sharkey    /**
87e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Intent action used to identify {@link DocumentsProvider} instances. This
88e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * is used in the {@code <intent-filter>} of a {@code <provider>}.
8985f5f8132015d8a5043ea4413702420d0d157c9fJeff Sharkey     */
9085f5f8132015d8a5043ea4413702420d0d157c9fJeff Sharkey    public static final String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
9185f5f8132015d8a5043ea4413702420d0d157c9fJeff Sharkey
925b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey    /** {@hide} */
9315be83612c34b65404f15d0feafdb4a329467769Jeff Sharkey    public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
9415be83612c34b65404f15d0feafdb4a329467769Jeff Sharkey
9596c620595bd0585f934b0971b4552c57845e9a78Jeff Sharkey    /** {@hide} */
961719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska    public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
971719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska
981719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska    /** {@hide} */
9983df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay    public static final String EXTRA_SHOW_FILESIZE = "android.content.extra.SHOW_FILESIZE";
10083df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay
10183df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay    /** {@hide} */
102f570c771f23792837fb63f708bfa5c948d5fcad7Steve McKay    public static final String EXTRA_FANCY_FEATURES = "android.content.extra.FANCY";
103f570c771f23792837fb63f708bfa5c948d5fcad7Steve McKay
104f570c771f23792837fb63f708bfa5c948d5fcad7Steve McKay    /** {@hide} */
10574fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
10674fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski
107c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey    /**
10877797400ec103b1691e1c3fa602c329b49ac18caBen Kwa     * Set this in a DocumentsUI intent to cause a package's own roots to be
10977797400ec103b1691e1c3fa602c329b49ac18caBen Kwa     * excluded from the roots list.
11077797400ec103b1691e1c3fa602c329b49ac18caBen Kwa     */
11177797400ec103b1691e1c3fa602c329b49ac18caBen Kwa    public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
11277797400ec103b1691e1c3fa602c329b49ac18caBen Kwa
11377797400ec103b1691e1c3fa602c329b49ac18caBen Kwa    /**
114c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * Included in {@link AssetFileDescriptor#getExtras()} when returned
115c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * thumbnail should be rotated.
116c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     *
117c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * @see MediaStore.Images.ImageColumns#ORIENTATION
118c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     */
1195f53f6531176d503d0bb26b50581091372a1478fTomasz Mikolajewski    public static final String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
120c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1210e591f95bd023fc153520efe0852180bb580a442Tomasz Mikolajewski    /**
1220e591f95bd023fc153520efe0852180bb580a442Tomasz Mikolajewski     * Overrides the default prompt text in DocumentsUI when set in an intent.
1230e591f95bd023fc153520efe0852180bb580a442Tomasz Mikolajewski     */
1240e591f95bd023fc153520efe0852180bb580a442Tomasz Mikolajewski    public static final String EXTRA_PROMPT = "android.provider.extra.PROMPT";
1250e591f95bd023fc153520efe0852180bb580a442Tomasz Mikolajewski
12615be83612c34b65404f15d0feafdb4a329467769Jeff Sharkey    /** {@hide} */
127a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
128aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
12959d577a518333f4b4514315b6d10e8dba160abcdJeff Sharkey    /** {@hide} */
13084cebbeb69e5b473f0cb4d1575bdc57aac48e32eBen Kwa    public static final String ACTION_BROWSE = "android.provider.action.BROWSE";
1311407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey
1321407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey    /** {@hide} */
1331407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey    public static final String
1341407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey            ACTION_DOCUMENT_ROOT_SETTINGS = "android.provider.action.DOCUMENT_ROOT_SETTINGS";
13559d577a518333f4b4514315b6d10e8dba160abcdJeff Sharkey
136bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey    /**
137de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey     * Buffer is large enough to rewind past any EXIF headers.
138de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey     */
139de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    private static final int THUMBNAIL_BUFFER_SIZE = (int) (128 * KB_IN_BYTES);
140de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
141cc2ae6b421353e03160df3b05ce9042b8a5ff8ccJeff Sharkey    /** {@hide} */
142cc2ae6b421353e03160df3b05ce9042b8a5ff8ccJeff Sharkey    public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
143cc2ae6b421353e03160df3b05ce9042b8a5ff8ccJeff Sharkey
144de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    /**
145e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Constants related to a document, including {@link Cursor} column names
146ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * and flags.
147ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * <p>
148e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * A document can be either an openable stream (with a specific MIME type),
149e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * or a directory containing additional documents (with the
150e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * {@link #MIME_TYPE_DIR} MIME type). A directory represents the top of a
151e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * subtree containing zero or more documents, which can recursively contain
152e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * even more documents and directories.
153ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * <p>
154ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * All columns are <em>read-only</em> to client applications.
155bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey     */
156ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public final static class Document {
157ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        private Document() {
158a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        }
1599ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
160a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
161ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Unique ID of a document. This ID is both provided by and interpreted
162ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * by a {@link DocumentsProvider}, and should be treated as an opaque
1636efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * value by client applications. This column is required.
164ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
165ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Each document must have a unique ID within a provider, but that
166ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * single document may be included as a child of multiple directories.
167ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
168ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * A provider must always return durable IDs, since they will be used to
169e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * issue long-term URI permission grants when an application interacts
170ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * with {@link Intent#ACTION_OPEN_DOCUMENT} and
171ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link Intent#ACTION_CREATE_DOCUMENT}.
172ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
173ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: STRING
174a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
175ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_DOCUMENT_ID = "document_id";
1769ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
177a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
178ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Concrete MIME type of a document. For example, "image/png" or
179ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * "application/pdf" for openable files. A document can also be a
180ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * directory containing additional documents, which is represented with
1816efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * the {@link #MIME_TYPE_DIR} MIME type. This column is required.
182ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
183ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: STRING
184a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
185ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #MIME_TYPE_DIR
186a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
187ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_MIME_TYPE = "mime_type";
18820d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey
189a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
190ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Display name of a document, used as the primary title displayed to a
1916efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * user. This column is required.
192ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
193ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: STRING
194ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
195ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_DISPLAY_NAME = OpenableColumns.DISPLAY_NAME;
196ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
197ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
1986efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Summary of a document, which may be shown to a user. This column is
1996efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * optional, and may be {@code null}.
200ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
201ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: STRING
202ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
203ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_SUMMARY = "summary";
204ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
205ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
206ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Timestamp when a document was last modified, in milliseconds since
2076efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * January 1, 1970 00:00:00.0 UTC. This column is required, and may be
2086efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * {@code null} if unknown. A {@link DocumentsProvider} can update this
2096efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * field using events from {@link OnCloseListener} or other reliable
210ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link ParcelFileDescriptor} transports.
211ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
212ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: INTEGER (long)
213a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
214ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see System#currentTimeMillis()
215a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
216ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_LAST_MODIFIED = "last_modified";
21720d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey
218a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
2196efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Specific icon resource ID for a document. This column is optional,
2206efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * and may be {@code null} to use a platform-provided default icon based
2216efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * on {@link #COLUMN_MIME_TYPE}.
222ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
223ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: INTEGER (int)
224ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
225ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_ICON = "icon";
226ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
227ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
2286efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Flags that apply to a document. This column is required.
229ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
230ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: INTEGER (int)
231a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
232ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #FLAG_SUPPORTS_WRITE
233ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #FLAG_SUPPORTS_DELETE
234ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #FLAG_SUPPORTS_THUMBNAIL
235ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #FLAG_DIR_PREFERS_GRID
2366efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
237a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         * @see #FLAG_VIRTUAL_DOCUMENT
238cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski         * @see #FLAG_SUPPORTS_COPY
239cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski         * @see #FLAG_SUPPORTS_MOVE
240cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski         * @see #FLAG_SUPPORTS_REMOVE
241a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
242ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_FLAGS = "flags";
2439ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
244a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
2456efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Size of a document, in bytes, or {@code null} if unknown. This column
2466efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * is required.
247ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
248ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: INTEGER (long)
249ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
250ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_SIZE = OpenableColumns.SIZE;
251ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
252ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
253ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * MIME type of a document which is a directory that may contain
254ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * additional documents.
255a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
256ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_MIME_TYPE
257a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
258ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String MIME_TYPE_DIR = "vnd.android.document/directory";
2595b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey
260a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
261ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Flag indicating that a document can be represented as a thumbnail.
262a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
263ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
264ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri,
265ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         *      Point, CancellationSignal)
266ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see DocumentsProvider#openDocumentThumbnail(String, Point,
267ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         *      android.os.CancellationSignal)
268a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
269ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final int FLAG_SUPPORTS_THUMBNAIL = 1;
270a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey
271a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
272aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         * Flag indicating that a document supports writing.
273ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
274ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * When a document is opened with {@link Intent#ACTION_OPEN_DOCUMENT},
275ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * the calling application is granted both
276ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and
277ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. However, the actual
278ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * writability of a document may change over time, for example due to
279ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * remote access changes. This flag indicates that a document client can
280ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * expect {@link ContentResolver#openOutputStream(Uri)} to succeed.
28183df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay         *
282ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
283a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
284ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final int FLAG_SUPPORTS_WRITE = 1 << 1;
285a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey
286a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
287ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Flag indicating that a document is deletable.
288a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
289ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
290ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
291ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see DocumentsProvider#deleteDocument(String)
292a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
293ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
29492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
295ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
296ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Flag indicating that a document is a directory that supports creation
297ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * of new files within it. Only valid when {@link #COLUMN_MIME_TYPE} is
298ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link #MIME_TYPE_DIR}.
299ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         *
300ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
301ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see DocumentsProvider#createDocument(String, String, String)
302ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
303ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3;
30492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
305ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
306ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Flag indicating that a directory prefers its contents be shown in a
307ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * larger format grid. Usually suitable when a directory contains mostly
308ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * pictures. Only valid when {@link #COLUMN_MIME_TYPE} is
309ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link #MIME_TYPE_DIR}.
310ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         *
311ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
312ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
3133e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey        public static final int FLAG_DIR_PREFERS_GRID = 1 << 4;
314d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
315d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        /**
316d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey         * Flag indicating that a directory prefers its contents be sorted by
317d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey         * {@link #COLUMN_LAST_MODIFIED}. Only valid when
318d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey         * {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
319d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey         *
320d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey         * @see #COLUMN_FLAGS
321d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey         */
3223e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey        public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 1 << 5;
323f6db154975ef575479ba4ab59d80bcf592288252Jeff Sharkey
324f6db154975ef575479ba4ab59d80bcf592288252Jeff Sharkey        /**
325b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey         * Flag indicating that a document can be renamed.
326b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey         *
327b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey         * @see #COLUMN_FLAGS
32890a753330d1cccdb844cedcb3e01ad642f2a5542Tomasz Mikolajewski         * @see DocumentsContract#renameDocument(ContentResolver, Uri,
329b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey         *      String)
330b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey         * @see DocumentsProvider#renameDocument(String, String)
331b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey         */
332b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        public static final int FLAG_SUPPORTS_RENAME = 1 << 6;
333b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey
334b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        /**
33574fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski         * Flag indicating that a document can be copied to another location
33674fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski         * within the same document provider.
33774fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski         *
33874fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski         * @see #COLUMN_FLAGS
33990a753330d1cccdb844cedcb3e01ad642f2a5542Tomasz Mikolajewski         * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri)
340a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski         * @see DocumentsProvider#copyDocument(String, String)
34174fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski         */
34274fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        public static final int FLAG_SUPPORTS_COPY = 1 << 7;
34374fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski
34474fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        /**
345a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski         * Flag indicating that a document can be moved to another location
346a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski         * within the same document provider.
347a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski         *
348a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski         * @see #COLUMN_FLAGS
34990a753330d1cccdb844cedcb3e01ad642f2a5542Tomasz Mikolajewski         * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri)
350d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski         * @see DocumentsProvider#moveDocument(String, String, String)
351a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski         */
352a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
353a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski
354a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        /**
355a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         * Flag indicating that a document is virtual, and doesn't have byte
356a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         * representation in the MIME type specified as {@link #COLUMN_MIME_TYPE}.
357a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         *
358a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         * @see #COLUMN_FLAGS
359a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         * @see #COLUMN_MIME_TYPE
360a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         * @see DocumentsProvider#openTypedDocument(String, String, Bundle,
361a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         *      android.os.CancellationSignal)
362d99964fd66dc4dabe431ff995995de508f614216Tomasz Mikolajewski         * @see DocumentsProvider#getDocumentStreamTypes(String, String)
363a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski         */
36475395651c272b3df746e0e5fede0a8fedd81e44bTomasz Mikolajewski        public static final int FLAG_VIRTUAL_DOCUMENT = 1 << 9;
365a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski
366a8057a9dcef71f9b3e0f31e830d750337a4349baTomasz Mikolajewski        /**
3679b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski         * Flag indicating that a document can be removed from a parent.
3689b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski         *
3699b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski         * @see #COLUMN_FLAGS
37090a753330d1cccdb844cedcb3e01ad642f2a5542Tomasz Mikolajewski         * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri)
3719b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski         * @see DocumentsProvider#removeDocument(String, String)
3729b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski         */
3739b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski        public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
3749b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski
3759b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski        /**
376a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * Flag indicating that a document is an archive, and it's contents can be
377a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * obtained via {@link DocumentsProvider#queryChildDocuments}.
378a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * <p>
379a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * The <em>provider</em> support library offers utility classes to add common
380a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * archive support.
381a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         *
382a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * @see #COLUMN_FLAGS
383a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         * @see DocumentsProvider#queryChildDocuments(String, String[], String)
3849b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski         * @hide
385a4c338aea611096dc67d445708f4474fd93e4d62Tomasz Mikolajewski         */
3869b055e1d842c5c68be168807bcf5b84a7c95db80Tomasz Mikolajewski        public static final int FLAG_ARCHIVE = 1 << 15;
387168e464812341fdc64c3b85659b60114bee3b86eSteve McKay
388168e464812341fdc64c3b85659b60114bee3b86eSteve McKay        /**
389168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         * Flag indicating that a document is not complete, likely its
390168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         * contents are being downloaded. Partial files cannot be opened,
391168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         * copied, moved in the UI. But they can be deleted and retried
392168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         * if they represent a failed download.
393168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         *
394168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         * @see #COLUMN_FLAGS
395168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         * @hide
396168e464812341fdc64c3b85659b60114bee3b86eSteve McKay         */
397168e464812341fdc64c3b85659b60114bee3b86eSteve McKay        public static final int FLAG_PARTIAL = 1 << 16;
398dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    }
399dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
400bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey    /**
401e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Constants related to a root of documents, including {@link Cursor} column
402e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * names and flags. A root is the start of a tree of documents, such as a
403e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * physical storage device, or an account. Each root starts at the directory
404e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * referenced by {@link Root#COLUMN_DOCUMENT_ID}, which can recursively
405e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * contain both documents and directories.
406ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * <p>
407ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * All columns are <em>read-only</em> to client applications.
408bd3b902567b09379e1b62c60b3319ad82102efadJeff Sharkey     */
409ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public final static class Root {
410ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        private Root() {
411dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey        }
4129ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
4139ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey        /**
414ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Unique ID of a root. This ID is both provided by and interpreted by a
415ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * {@link DocumentsProvider}, and should be treated as an opaque value
4166efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * by client applications. This column is required.
4179ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         * <p>
4189ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         * Type: STRING
4199ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         */
420ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_ROOT_ID = "root_id";
4219ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
4229ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey        /**
4236efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Flags that apply to a root. This column is required.
4249ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         * <p>
425ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: INTEGER (int)
4269ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         *
427ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #FLAG_LOCAL_ONLY
428ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #FLAG_SUPPORTS_CREATE
4293e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * @see #FLAG_SUPPORTS_RECENTS
4303e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * @see #FLAG_SUPPORTS_SEARCH
4319ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         */
432ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_FLAGS = "flags";
4339ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
4349ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey        /**
4356efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Icon resource ID for a root. This column is required.
4369ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         * <p>
4379ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         * Type: INTEGER (int)
4389ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey         */
439ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_ICON = "icon";
44066516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey
44166516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey        /**
4426efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Title for a root, which will be shown to a user. This column is
443e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * required. For a single storage service surfacing multiple accounts as
444e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * different roots, this title should be the name of the service.
44566516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey         * <p>
44666516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey         * Type: STRING
44766516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey         */
448ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_TITLE = "title";
449aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
450aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        /**
4516efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Summary for this root, which may be shown to a user. This column is
452e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * optional, and may be {@code null}. For a single storage service
453e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * surfacing multiple accounts as different roots, this summary should
454e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * be the name of the account.
455aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         * <p>
456ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: STRING
457aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         */
458ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_SUMMARY = "summary";
4599ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
460a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
461ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Document which is a directory that represents the top directory of
4626efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * this root. This column is required.
463ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
464ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: STRING
465a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         *
466ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see Document#COLUMN_DOCUMENT_ID
467a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey         */
468ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_DOCUMENT_ID = "document_id";
469ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
470ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        /**
4716efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Number of bytes available in this root. This column is optional, and
4726efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * may be {@code null} if unknown or unbounded.
473ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * <p>
474ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * Type: INTEGER (long)
475ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         */
476ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public static final String COLUMN_AVAILABLE_BYTES = "available_bytes";
47766516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey
478a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey        /**
4793f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski         * Capacity of a root in bytes. This column is optional, and may be
4803f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski         * {@code null} if unknown or unbounded.
4813f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski         * <p>
4823f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski         * Type: INTEGER (long)
4833f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski         */
4843f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski        public static final String COLUMN_CAPACITY_BYTES = "capacity_bytes";
4853f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski
4863f78e175ac647daf7184aaf6ba768777cfdd2ff5Tomasz Mikolajewski        /**
4876efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * MIME types supported by this root. This column is optional, and if
4886efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * {@code null} the root is assumed to support all MIME types. Multiple
4896efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * MIME types can be separated by a newline. For example, a root
4906efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * supporting audio might return "audio/*\napplication/x-flac".
491923396b7720367ec99c889725fddfa556e703971Jeff Sharkey         * <p>
4926efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * Type: STRING
493923396b7720367ec99c889725fddfa556e703971Jeff Sharkey         */
494923396b7720367ec99c889725fddfa556e703971Jeff Sharkey        public static final String COLUMN_MIME_TYPES = "mime_types";
495923396b7720367ec99c889725fddfa556e703971Jeff Sharkey
496a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        /** {@hide} */
497a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        public static final String MIME_TYPE_ITEM = "vnd.android.document/root";
498a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey
499923396b7720367ec99c889725fddfa556e703971Jeff Sharkey        /**
500aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         * Flag indicating that at least one directory under this root supports
501ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * creating content. Roots with this flag will be shown when an
502ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}.
503aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         *
504ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
505aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         */
506aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        public static final int FLAG_SUPPORTS_CREATE = 1;
50792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
50820d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey        /**
509aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         * Flag indicating that this root offers content that is strictly local
510aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey         * on the device. That is, no network requests are made for the content.
51120d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey         *
512ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see #COLUMN_FLAGS
513ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey         * @see Intent#EXTRA_LOCAL_ONLY
51420d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey         */
515aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        public static final int FLAG_LOCAL_ONLY = 1 << 1;
516aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
51720d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey        /**
518e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * Flag indicating that this root can be queried to provide recently
519e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * modified documents.
520251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey         *
521251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey         * @see #COLUMN_FLAGS
522251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey         * @see DocumentsContract#buildRecentDocumentsUri(String, String)
523e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * @see DocumentsProvider#queryRecentDocuments(String, String[])
524251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey         */
5256efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey        public static final int FLAG_SUPPORTS_RECENTS = 1 << 2;
5263e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey
5273e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey        /**
5283e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * Flag indicating that this root supports search.
5293e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         *
5303e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * @see #COLUMN_FLAGS
531e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         * @see DocumentsContract#buildSearchDocumentsUri(String, String,
532e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey         *      String)
5333e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * @see DocumentsProvider#querySearchDocuments(String, String,
5343e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         *      String[])
5353e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         */
5366efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey        public static final int FLAG_SUPPORTS_SEARCH = 1 << 3;
5373e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey
5383e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey        /**
539b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey         * Flag indicating that this root supports testing parent child
540b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey         * relationships.
54121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey         *
54221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey         * @see #COLUMN_FLAGS
54321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey         * @see DocumentsProvider#isChildDocument(String, String)
54421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey         */
545b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey        public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4;
54621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
54721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        /**
5483e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * Flag indicating that this root is currently empty. This may be used
5493e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * to hide the root when opening documents, but the root will still be
5503e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is
5516efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * also set. If the value of this flag changes, such as when a root
5526efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * becomes non-empty, you must send a content changed notification for
5536efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * {@link DocumentsContract#buildRootsUri(String)}.
5543e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         *
5553e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey         * @see #COLUMN_FLAGS
5566efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * @see ContentResolver#notifyChange(Uri,
5576efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         *      android.database.ContentObserver, boolean)
5586efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         * @hide
5596efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey         */
5606efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey        public static final int FLAG_EMPTY = 1 << 16;
5616efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey
5626efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey        /**
5631719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska         * Flag indicating that this root should only be visible to advanced
5641719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska         * users.
5651719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska         *
5661719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska         * @see #COLUMN_FLAGS
5671719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska         * @hide
5681719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska         */
5691719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska        public static final int FLAG_ADVANCED = 1 << 17;
5701719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska
5711719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska        /**
5721407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey         * Flag indicating that this root has settings.
5731407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey         *
5741407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey         * @see #COLUMN_FLAGS
5751407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey         * @see DocumentsContract#ACTION_DOCUMENT_ROOT_SETTINGS
5761407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey         * @hide
5771407d4c77732c7fad34c6b813d05771de6ed970fJeff Sharkey         */
5781719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska        public static final int FLAG_HAS_SETTINGS = 1 << 18;
579ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay
580ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay        /**
581ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         * Flag indicating that this root is on removable SD card storage.
582ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         *
583ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         * @see #COLUMN_FLAGS
584ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         * @hide
585ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         */
5861719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska        public static final int FLAG_REMOVABLE_SD = 1 << 19;
587ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay
588ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay        /**
589ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         * Flag indicating that this root is on removable USB storage.
590ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         *
591ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         * @see #COLUMN_FLAGS
592ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         * @hide
593ba23e54d263fefaac96b3ce6b068e70ec6f06128Steve McKay         */
5941719b3555dc9bff5394045585051e7d5684bceb1Aga Wronska        public static final int FLAG_REMOVABLE_USB = 1 << 20;
595ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
596aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
597ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
598ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Optional boolean flag included in a directory {@link Cursor#getExtras()}
599ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * indicating that a document provider is still loading data. For example, a
600ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * provider has returned some results, but is still waiting on an
601ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * outstanding network request. The provider must send a content changed
602ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * notification when loading is finished.
603ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
604ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see ContentResolver#notifyChange(Uri, android.database.ContentObserver,
605ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *      boolean)
606ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
607ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static final String EXTRA_LOADING = "loading";
608aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
609ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
610ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Optional string included in a directory {@link Cursor#getExtras()}
611ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * providing an informational message that should be shown to a user. For
612ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * example, a provider may wish to indicate that not all documents are
613ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * available.
614ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
615ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static final String EXTRA_INFO = "info";
616aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
617ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
618ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Optional string included in a directory {@link Cursor#getExtras()}
619ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * providing an error message that should be shown to a user. For example, a
620ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * provider may wish to indicate that a network error occurred. The user may
621ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * choose to retry, resulting in a new query.
622ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
623ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static final String EXTRA_ERROR = "error";
624aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
625d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    /**
626d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay     * Optional result (I'm thinking boolean) answer to a question.
627d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay     * {@hide}
628d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay     */
629d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    public static final String EXTRA_RESULT = "result";
630d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay
631ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /** {@hide} */
632ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static final String METHOD_CREATE_DOCUMENT = "android:createDocument";
633ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /** {@hide} */
634b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    public static final String METHOD_RENAME_DOCUMENT = "android:renameDocument";
635b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    /** {@hide} */
636ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
63774fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    /** {@hide} */
63874fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    public static final String METHOD_COPY_DOCUMENT = "android:copyDocument";
639a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    /** {@hide} */
640a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    public static final String METHOD_MOVE_DOCUMENT = "android:moveDocument";
641d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    /** {@hide} */
642d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
643cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    /** {@hide} */
644cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    public static final String METHOD_REMOVE_DOCUMENT = "android:removeDocument";
645aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
646ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /** {@hide} */
647d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski    public static final String EXTRA_PARENT_URI = "parentUri";
648d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski    /** {@hide} */
64921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    public static final String EXTRA_URI = "uri";
650aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
651ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private static final String PATH_ROOT = "root";
652ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private static final String PATH_RECENT = "recent";
653ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private static final String PATH_DOCUMENT = "document";
654ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private static final String PATH_CHILDREN = "children";
655ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private static final String PATH_SEARCH = "search";
656b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    private static final String PATH_TREE = "tree";
657aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
658ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private static final String PARAM_QUERY = "query";
6594ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    private static final String PARAM_MANAGE = "manage";
660aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
661ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
662e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Build URI representing the roots of a document provider. When queried, a
663ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * provider will return one or more rows with columns defined by
664ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * {@link Root}.
665ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
666ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see DocumentsProvider#queryRoots(String[])
667ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
668ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Uri buildRootsUri(String authority) {
669ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
670ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .authority(authority).appendPath(PATH_ROOT).build();
671ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
672aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
673ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
674e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Build URI representing the given {@link Root#COLUMN_ROOT_ID} in a
675a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey     * document provider.
676a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey     *
677a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey     * @see #getRootId(Uri)
678a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey     */
679a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    public static Uri buildRootUri(String authority, String rootId) {
680a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
681a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey                .authority(authority).appendPath(PATH_ROOT).appendPath(rootId).build();
682a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    }
683a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey
684a61dc8e03e6e863005b3a4629ca8f3801d33d3c4Jeff Sharkey    /**
685b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay     * Builds URI for user home directory on external (local) storage.
686b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay     * {@hide}
687b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay     */
688b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay    public static Uri buildHomeUri() {
689b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // TODO: Avoid this type of interpackage copying. Added here to avoid
690b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // direct coupling, but not ideal.
691b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        return DocumentsContract.buildRootUri("com.android.externalstorage.documents", "home");
692b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay    }
693b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay
694b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay    /**
695e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Build URI representing the recently modified documents of a specific root
6963e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * in a document provider. When queried, a provider will return zero or more
6973e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * rows with columns defined by {@link Document}.
698ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
699ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see DocumentsProvider#queryRecentDocuments(String, String[])
700ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #getRootId(Uri)
701ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
702ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Uri buildRecentDocumentsUri(String authority, String rootId) {
703ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
704ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .authority(authority).appendPath(PATH_ROOT).appendPath(rootId)
705ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .appendPath(PATH_RECENT).build();
706ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
707ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
708ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
70921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Build URI representing access to descendant documents of the given
71021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID}.
71121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
712b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @see #getTreeDocumentId(Uri)
71321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
714b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    public static Uri buildTreeDocumentUri(String authority, String documentId) {
71521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
716b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey                .appendPath(PATH_TREE).appendPath(documentId).build();
71721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
71821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
71921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
720b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in
721b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * a document provider. When queried, a provider will return a single row
722b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * with columns defined by {@link Document}.
723ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
724ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see DocumentsProvider#queryDocument(String, String[])
725ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #getDocumentId(Uri)
726ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
727ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Uri buildDocumentUri(String authority, String documentId) {
728ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
729ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .authority(authority).appendPath(PATH_DOCUMENT).appendPath(documentId).build();
730ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
731ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
732ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
733b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in
734b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * a document provider. When queried, a provider will return a single row
735b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * with columns defined by {@link Document}.
736b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * <p>
737b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * However, instead of directly accessing the target document, the returned
738b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * URI will leverage access granted through a subtree URI, typically
739b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target document
740b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * must be a descendant (child, grandchild, etc) of the subtree.
74121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * <p>
74221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * This is typically used to access documents under a user-selected
743b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * directory tree, since it doesn't require the user to separately confirm
744b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * each new document access.
74521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
746b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @param treeUri the subtree to leverage to gain access to the target
747b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     *            document. The target directory must be a descendant of this
748b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     *            subtree.
74921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @param documentId the target document, which the caller may not have
75021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *            direct access to.
751b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
75221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see DocumentsProvider#isChildDocument(String, String)
75321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see #buildDocumentUri(String, String)
75421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
755b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) {
75621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
757b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey                .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
758b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey                .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
75921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                .appendPath(documentId).build();
76021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
76121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
76221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /** {@hide} */
763b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    public static Uri buildDocumentUriMaybeUsingTree(Uri baseUri, String documentId) {
764b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey        if (isTreeUri(baseUri)) {
765b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey            return buildDocumentUriUsingTree(baseUri, documentId);
76621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        } else {
76721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            return buildDocumentUri(baseUri.getAuthority(), documentId);
76821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        }
76921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
77021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
77121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
772b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * Build URI representing the children of the target directory in a document
773ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * provider. When queried, a provider will return zero or more rows with
774ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * columns defined by {@link Document}.
775ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
776ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param parentDocumentId the document to return children for, which must
777ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            be a directory with MIME type of
778ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            {@link Document#MIME_TYPE_DIR}.
779ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see DocumentsProvider#queryChildDocuments(String, String[], String)
780ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #getDocumentId(Uri)
781ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
782ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Uri buildChildDocumentsUri(String authority, String parentDocumentId) {
783ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
784ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_CHILDREN)
785ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .build();
786ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
787aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
788ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
789b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * Build URI representing the children of the target directory in a document
790b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * provider. When queried, a provider will return zero or more rows with
791b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * columns defined by {@link Document}.
792b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * <p>
793b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * However, instead of directly accessing the target directory, the returned
794b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * URI will leverage access granted through a subtree URI, typically
795b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target
796b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * directory must be a descendant (child, grandchild, etc) of the subtree.
79721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * <p>
79821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * This is typically used to access documents under a user-selected
799b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * directory tree, since it doesn't require the user to separately confirm
800b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * each new document access.
80121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
802b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @param treeUri the subtree to leverage to gain access to the target
803b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     *            document. The target directory must be a descendant of this
804b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     *            subtree.
805b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @param parentDocumentId the document to return children for, which the
806b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     *            caller may not have direct access to, and which must be a
807b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     *            directory with MIME type of {@link Document#MIME_TYPE_DIR}.
808b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
80921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see DocumentsProvider#isChildDocument(String, String)
81021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see #buildChildDocumentsUri(String, String)
81121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
812b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    public static Uri buildChildDocumentsUriUsingTree(Uri treeUri, String parentDocumentId) {
81321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
814b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey                .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
815b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey                .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
81621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                .appendPath(parentDocumentId).appendPath(PATH_CHILDREN).build();
81721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
81821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
81921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
820e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Build URI representing a search for matching documents under a specific
8213e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * root in a document provider. When queried, a provider will return zero or
8223e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * more rows with columns defined by {@link Document}.
823ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *
824ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see DocumentsProvider#querySearchDocuments(String, String, String[])
8253e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey     * @see #getRootId(Uri)
826ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see #getSearchDocumentsQuery(Uri)
827ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
828ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Uri buildSearchDocumentsUri(
8293e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey            String authority, String rootId, String query) {
830ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
8313e1189b3590aefb65a2af720ae2ba959bbd4188dJeff Sharkey                .appendPath(PATH_ROOT).appendPath(rootId).appendPath(PATH_SEARCH)
832ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                .appendQueryParameter(PARAM_QUERY, query).build();
833ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
834ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey
835ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
836e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Test if the given URI represents a {@link Document} backed by a
837ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey     * {@link DocumentsProvider}.
83821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
83921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see #buildDocumentUri(String, String)
840b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey     * @see #buildDocumentUriUsingTree(Uri, String)
841ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey     */
842323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    public static boolean isDocumentUri(Context context, @Nullable Uri uri) {
843323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
844323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            final List<String> paths = uri.getPathSegments();
845323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            if (paths.size() == 2) {
846323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay                return PATH_DOCUMENT.equals(paths.get(0));
847323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            } else if (paths.size() == 4) {
848323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay                return PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2));
849323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            }
850b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey        }
851323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        return false;
852323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    }
853323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay
854323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    /** {@hide} */
855323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    public static boolean isRootUri(Context context, @Nullable Uri uri) {
856323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
857323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            final List<String> paths = uri.getPathSegments();
858323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0)));
859ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey        }
86021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return false;
86121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
86221de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
86321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /** {@hide} */
864323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    public static boolean isContentUri(@Nullable Uri uri) {
865323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme());
866323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    }
867323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay
8687db9c8193797ad8bca6d191d5bbb2ec2f7dd1868Tomasz Mikolajewski    /**
8697db9c8193797ad8bca6d191d5bbb2ec2f7dd1868Tomasz Mikolajewski     * Test if the given URI represents a {@link Document} tree.
8707db9c8193797ad8bca6d191d5bbb2ec2f7dd1868Tomasz Mikolajewski     *
8717db9c8193797ad8bca6d191d5bbb2ec2f7dd1868Tomasz Mikolajewski     * @see #buildTreeDocumentUri(String, String)
87290a753330d1cccdb844cedcb3e01ad642f2a5542Tomasz Mikolajewski     * @see #getTreeDocumentId(Uri)
8737db9c8193797ad8bca6d191d5bbb2ec2f7dd1868Tomasz Mikolajewski     */
874b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    public static boolean isTreeUri(Uri uri) {
87521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final List<String> paths = uri.getPathSegments();
876b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey        return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
87721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
878ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey
87921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    private static boolean isDocumentsProvider(Context context, String authority) {
880d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkey        final Intent intent = new Intent(PROVIDER_INTERFACE);
881d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkey        final List<ResolveInfo> infos = context.getPackageManager()
882d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkey                .queryIntentContentProviders(intent, 0);
883d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkey        for (ResolveInfo info : infos) {
88421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            if (authority.equals(info.providerInfo.authority)) {
885d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkey                return true;
886d2e1e810a79ce0988df57c0bcbcd184e14a067b4Jeff Sharkey            }
887ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey        }
888ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey        return false;
889ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey    }
890ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey
891ee2f7df9ee8a4f43c3b0858bad08a4f0a59a627fJeff Sharkey    /**
892e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Extract the {@link Root#COLUMN_ROOT_ID} from the given URI.
893ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
894ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static String getRootId(Uri rootUri) {
895ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final List<String> paths = rootUri.getPathSegments();
89621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        if (paths.size() >= 2 && PATH_ROOT.equals(paths.get(0))) {
89721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            return paths.get(1);
898aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        }
89921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        throw new IllegalArgumentException("Invalid URI: " + rootUri);
900ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
901aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
902ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
903e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Extract the {@link Document#COLUMN_DOCUMENT_ID} from the given URI.
90421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     *
90521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * @see #isDocumentUri(Context, Uri)
906ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
907ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static String getDocumentId(Uri documentUri) {
908ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final List<String> paths = documentUri.getPathSegments();
90921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        if (paths.size() >= 2 && PATH_DOCUMENT.equals(paths.get(0))) {
91021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            return paths.get(1);
911ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        }
912b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey        if (paths.size() >= 4 && PATH_TREE.equals(paths.get(0))
91321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey                && PATH_DOCUMENT.equals(paths.get(2))) {
91421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            return paths.get(3);
915ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        }
91621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        throw new IllegalArgumentException("Invalid URI: " + documentUri);
91721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    }
91821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey
91921de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey    /**
92021de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     * Extract the via {@link Document#COLUMN_DOCUMENT_ID} from the given URI.
92121de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey     */
922b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey    public static String getTreeDocumentId(Uri documentUri) {
92321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        final List<String> paths = documentUri.getPathSegments();
924b9fbb7290b02de1ce621deaa2d28a5e42f2e0937Jeff Sharkey        if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) {
92521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey            return paths.get(1);
92621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        }
92721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        throw new IllegalArgumentException("Invalid URI: " + documentUri);
928ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    }
929aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
930ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    /**
931e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Extract the search query from a URI built by
932ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * {@link #buildSearchDocumentsUri(String, String, String)}.
933ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     */
934ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static String getSearchDocumentsQuery(Uri searchDocumentsUri) {
935ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        return searchDocumentsUri.getQueryParameter(PARAM_QUERY);
93620d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey    }
93720d96d8aff2193d548977e23ce5158657cac94e0Jeff Sharkey
9384ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    /** {@hide} */
9394ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    public static Uri setManageMode(Uri uri) {
9404ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey        return uri.buildUpon().appendQueryParameter(PARAM_MANAGE, "true").build();
9414ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    }
9424ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey
9434ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    /** {@hide} */
9444ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    public static boolean isManageMode(Uri uri) {
9454ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey        return uri.getBooleanQueryParameter(PARAM_MANAGE, false);
9464ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey    }
9474ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey
9489ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey    /**
949e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * Return thumbnail representing the document at the given URI. Callers are
950ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * responsible for their own in-memory caching.
9519ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey     *
952ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param documentUri document to return thumbnail for, which must have
953ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            {@link Document#FLAG_SUPPORTS_THUMBNAIL} set.
954ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param size optimal thumbnail size desired. A provider may return a
955ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            thumbnail of a different size, but never more than double the
956ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            requested size.
957e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372Jeff Sharkey     * @param signal signal used to indicate if caller is no longer interested
958ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            in the thumbnail.
9599ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey     * @return decoded thumbnail, or {@code null} if problem was encountered.
960ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @see DocumentsProvider#openDocumentThumbnail(String, Point,
961ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *      android.os.CancellationSignal)
9629ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey     */
963ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Bitmap getDocumentThumbnail(
964ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal) {
965de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
966de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                documentUri.getAuthority());
967de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        try {
968de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            return getDocumentThumbnail(client, documentUri, size, signal);
9697aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        } catch (Exception e) {
97033819318f61224cdda45e5e9a70dc9cdb9f33911Jeff Sharkey            if (!(e instanceof OperationCanceledException)) {
97133819318f61224cdda45e5e9a70dc9cdb9f33911Jeff Sharkey                Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
97233819318f61224cdda45e5e9a70dc9cdb9f33911Jeff Sharkey            }
973de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            return null;
974de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        } finally {
9757aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            ContentProviderClient.releaseQuietly(client);
976de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        }
977de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    }
978de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
979de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    /** {@hide} */
980de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    public static Bitmap getDocumentThumbnail(
981de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
9827aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            throws RemoteException, IOException {
9836398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey        final Bundle openOpts = new Bundle();
9845b836f29a3f56f9d13b6004417330c41ec0c18aeJeff Sharkey        openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size);
9859ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
9869d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        AssetFileDescriptor afd = null;
987c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        Bitmap bitmap = null;
9889ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey        try {
989de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
9909d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
9919d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            final FileDescriptor fd = afd.getFileDescriptor();
9926398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            final long offset = afd.getStartOffset();
993de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
994de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            // Try seeking on the returned FD, since it gives us the most
995de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            // optimal decode path; otherwise fall back to buffering.
996de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            BufferedInputStream is = null;
997de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            try {
99834385d352da19805ae948215e2edbeedd16b7941Elliott Hughes                Os.lseek(fd, offset, SEEK_SET);
999de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            } catch (ErrnoException e) {
1000de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE);
1001de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                is.mark(THUMBNAIL_BUFFER_SIZE);
10026398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            }
10039d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
10046398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            // We requested a rough thumbnail size, but the remote size may have
10056398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            // returned something giant, so defensively scale down as needed.
10066398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            final BitmapFactory.Options opts = new BitmapFactory.Options();
10076398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            opts.inJustDecodeBounds = true;
1008de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            if (is != null) {
1009de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                BitmapFactory.decodeStream(is, null, opts);
10106398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            } else {
10116398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey                BitmapFactory.decodeFileDescriptor(fd, null, opts);
10126398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            }
10139d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
10146398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            final int widthSample = opts.outWidth / size.x;
10156398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            final int heightSample = opts.outHeight / size.y;
10169d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
10176398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            opts.inJustDecodeBounds = false;
10186398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            opts.inSampleSize = Math.min(widthSample, heightSample);
1019de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            if (is != null) {
1020de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                is.reset();
1021c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                bitmap = BitmapFactory.decodeStream(is, null, opts);
10226398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            } else {
1023de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                try {
102434385d352da19805ae948215e2edbeedd16b7941Elliott Hughes                    Os.lseek(fd, offset, SEEK_SET);
1025de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                } catch (ErrnoException e) {
1026de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                    e.rethrowAsIOException();
1027de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                }
1028c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts);
1029c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            }
1030c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1031c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            // Transform the bitmap if requested. We use a side-channel to
1032c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            // communicate the orientation, since EXIF thumbnails don't contain
1033c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            // the rotation flags of the original image.
1034c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            final Bundle extras = afd.getExtras();
1035c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
1036c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            if (orientation != 0) {
1037c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                final int width = bitmap.getWidth();
1038c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                final int height = bitmap.getHeight();
1039c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1040c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                final Matrix m = new Matrix();
1041c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                m.setRotate(orientation, width / 2, height / 2);
1042c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
10436398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey            }
10449ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey        } finally {
10459d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            IoUtils.closeQuietly(afd);
10469ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey        }
1047c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1048c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        return bitmap;
10499ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey    }
10509ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey
10519ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey    /**
1052ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * Create a new document with given MIME type and display name.
10535b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey     *
1054ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param parentDocumentUri directory with
1055ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     *            {@link Document#FLAG_DIR_SUPPORTS_CREATE}
1056aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param mimeType MIME type of new document
1057aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @param displayName name of new document
1058aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * @return newly created document, or {@code null} if failed
10595b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey     */
1060ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri,
1061ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            String mimeType, String displayName) {
1062de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
1063de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                parentDocumentUri.getAuthority());
1064de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        try {
1065de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey            return createDocument(client, parentDocumentUri, mimeType, displayName);
10667aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        } catch (Exception e) {
10677aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            Log.w(TAG, "Failed to create document", e);
10687aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            return null;
1069de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        } finally {
10707aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            ContentProviderClient.releaseQuietly(client);
1071de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        }
1072de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    }
1073de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
1074de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    /** {@hide} */
1075de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    public static Uri createDocument(ContentProviderClient client, Uri parentDocumentUri,
10767aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            String mimeType, String displayName) throws RemoteException {
1077aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        final Bundle in = new Bundle();
107821de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
1079ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        in.putString(Document.COLUMN_MIME_TYPE, mimeType);
1080ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
1081aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
10827aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        final Bundle out = client.call(METHOD_CREATE_DOCUMENT, null, in);
108321de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        return out.getParcelable(DocumentsContract.EXTRA_URI);
10845b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey    }
10855b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey
1086d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    /** {@hide} */
1087d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    public static boolean isChildDocument(ContentProviderClient client, Uri parentDocumentUri,
1088d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay            Uri childDocumentUri) throws RemoteException {
1089d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay
1090d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        final Bundle in = new Bundle();
1091d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
1092d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri);
1093d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay
1094d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        final Bundle out = client.call(METHOD_IS_CHILD_DOCUMENT, null, in);
1095d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        if (out == null) {
1096d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay            throw new RemoteException("Failed to get a reponse from isChildDocument query.");
1097d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        }
1098d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
1099d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay            throw new RemoteException("Response did not include result field..");
1100d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        }
1101d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay        return out.getBoolean(DocumentsContract.EXTRA_RESULT);
1102d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay    }
1103d3afdeebeb9dcfbb5f24e4afac988e2e96de26deSteve McKay
11045b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey    /**
1105b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * Change the display name of an existing document.
1106b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * <p>
1107b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * If the underlying provider needs to create a new
1108b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * {@link Document#COLUMN_DOCUMENT_ID} to represent the updated display
1109b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * name, that new document is returned and the original document is no
1110b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * longer valid. Otherwise, the original document is returned.
1111b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     *
1112b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * @param documentUri document with {@link Document#FLAG_SUPPORTS_RENAME}
1113b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * @param displayName updated name for document
1114b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     * @return the existing or new document after the rename, or {@code null} if
1115b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     *         failed.
1116b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey     */
1117b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    public static Uri renameDocument(ContentResolver resolver, Uri documentUri,
1118b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey            String displayName) {
1119b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
1120b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey                documentUri.getAuthority());
1121b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        try {
1122b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey            return renameDocument(client, documentUri, displayName);
1123b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        } catch (Exception e) {
1124b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey            Log.w(TAG, "Failed to rename document", e);
1125b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey            return null;
1126b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        } finally {
1127b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey            ContentProviderClient.releaseQuietly(client);
1128b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        }
1129b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    }
1130b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey
1131b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    /** {@hide} */
1132b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    public static Uri renameDocument(ContentProviderClient client, Uri documentUri,
1133b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey            String displayName) throws RemoteException {
1134b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        final Bundle in = new Bundle();
1135b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
1136b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
1137b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey
1138b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        final Bundle out = client.call(METHOD_RENAME_DOCUMENT, null, in);
1139b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        final Uri outUri = out.getParcelable(DocumentsContract.EXTRA_URI);
1140b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey        return (outUri != null) ? outUri : documentUri;
1141b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    }
1142b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey
1143b7e1255d5c8d9e4fa8dd389afb9f5aab35434df3Jeff Sharkey    /**
1144aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     * Delete the given document.
1145aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey     *
1146ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey     * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
11477aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey     * @return if the document was deleted successfully.
11485b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey     */
1149ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    public static boolean deleteDocument(ContentResolver resolver, Uri documentUri) {
1150de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
1151de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey                documentUri.getAuthority());
1152de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        try {
11537aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            deleteDocument(client, documentUri);
11547aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            return true;
11557aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        } catch (Exception e) {
11567aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            Log.w(TAG, "Failed to delete document", e);
11577aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            return false;
1158de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        } finally {
11597aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            ContentProviderClient.releaseQuietly(client);
1160de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey        }
1161de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    }
1162de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey
1163de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey    /** {@hide} */
11647aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey    public static void deleteDocument(ContentProviderClient client, Uri documentUri)
11657aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey            throws RemoteException {
1166aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey        final Bundle in = new Bundle();
116721de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
1168aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey
11697aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey        client.call(METHOD_DELETE_DOCUMENT, null, in);
11705b83f854d9cbd6dc9e5a31892dbe8515b4c29683Jeff Sharkey    }
1171c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1172c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey    /**
117374fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     * Copies the given document.
117474fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     *
117574fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_COPY}
117674fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     * @param targetParentDocumentUri document which will become a parent of the source
117774fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     *         document's copy.
117874fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     * @return the copied document, or {@code null} if failed.
117974fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski     */
118074fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
118174fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski            Uri targetParentDocumentUri) {
118274fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
118374fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski                sourceDocumentUri.getAuthority());
118474fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        try {
118574fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski            return copyDocument(client, sourceDocumentUri, targetParentDocumentUri);
118674fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        } catch (Exception e) {
118774fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski            Log.w(TAG, "Failed to copy document", e);
118874fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski            return null;
118974fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        } finally {
119074fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski            ContentProviderClient.releaseQuietly(client);
119174fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        }
119274fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    }
119374fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski
119474fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    /** {@hide} */
119574fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    public static Uri copyDocument(ContentProviderClient client, Uri sourceDocumentUri,
119674fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski            Uri targetParentDocumentUri) throws RemoteException {
119774fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        final Bundle in = new Bundle();
119874fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
119974fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
120074fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski
120174fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        final Bundle out = client.call(METHOD_COPY_DOCUMENT, null, in);
120274fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski        return out.getParcelable(DocumentsContract.EXTRA_URI);
120374fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    }
120474fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski
120574fe1816e84fc097adc5af1b6e866b4a219dca91Tomasz Mikolajewski    /**
1206a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     * Moves the given document under a new parent.
1207a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     *
1208a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE}
1209d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski     * @param sourceParentDocumentUri parent document of the document to move.
1210a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     * @param targetParentDocumentUri document which will become a new parent of the source
1211a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     *         document.
1212a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     * @return the moved document, or {@code null} if failed.
1213a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski     */
1214a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
1215d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) {
1216a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
1217a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski                sourceDocumentUri.getAuthority());
1218a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        try {
1219dcb4efeed54dd17c9af1fe75625d179d3f042f2bTomasz Mikolajewski            return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
1220d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski                    targetParentDocumentUri);
1221a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        } catch (Exception e) {
1222a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski            Log.w(TAG, "Failed to move document", e);
1223a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski            return null;
1224a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        } finally {
1225a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski            ContentProviderClient.releaseQuietly(client);
1226a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        }
1227a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    }
1228a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski
1229a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    /** {@hide} */
1230a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
1231d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException {
1232a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        final Bundle in = new Bundle();
1233a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
1234d46ecbcc5322cf817e64591e985f1f2a6167e9a7Tomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
1235a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
1236a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski
1237a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
1238a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski        return out.getParcelable(DocumentsContract.EXTRA_URI);
1239a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    }
1240a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski
1241a375a9988419791649ed7f42b662f1afe5953982Tomasz Mikolajewski    /**
1242cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     * Removes the given document from a parent directory.
1243cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     *
1244cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     * <p>In contrast to {@link #deleteDocument} it requires specifying the parent.
1245cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     * This method is especially useful if the document can be in multiple parents.
1246cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     *
1247cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     * @param documentUri document with {@link Document#FLAG_SUPPORTS_REMOVE}
1248cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     * @param parentDocumentUri parent document of the document to remove.
1249cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     * @return true if the document was removed successfully.
1250cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski     */
1251cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    public static boolean removeDocument(ContentResolver resolver, Uri documentUri,
1252cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            Uri parentDocumentUri) {
1253cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
1254cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski                documentUri.getAuthority());
1255cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        try {
1256cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            removeDocument(client, documentUri, parentDocumentUri);
1257cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            return true;
1258cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        } catch (Exception e) {
1259cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            Log.w(TAG, "Failed to remove document", e);
1260cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            return false;
1261cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        } finally {
1262cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            ContentProviderClient.releaseQuietly(client);
1263cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        }
1264cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    }
1265cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski
1266cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    /** {@hide} */
1267cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    public static void removeDocument(ContentProviderClient client, Uri documentUri,
1268cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski            Uri parentDocumentUri) throws RemoteException {
1269cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        final Bundle in = new Bundle();
1270cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
1271cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, parentDocumentUri);
1272cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski
1273cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski        client.call(METHOD_REMOVE_DOCUMENT, null, in);
1274cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    }
1275cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski
1276cbcd39488b4bddfaa84dfe378ede2f707aedd6caTomasz Mikolajewski    /**
1277c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * Open the given image for thumbnail purposes, using any embedded EXIF
1278c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * thumbnail if available, and providing orientation hints from the parent
1279c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * image.
1280c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     *
1281c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     * @hide
1282c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey     */
1283c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey    public static AssetFileDescriptor openImageThumbnail(File file) throws FileNotFoundException {
1284c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
1285c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                file, ParcelFileDescriptor.MODE_READ_ONLY);
1286c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        Bundle extras = null;
1287c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1288c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        try {
1289c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            final ExifInterface exif = new ExifInterface(file.getAbsolutePath());
1290c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1291c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
1292c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                case ExifInterface.ORIENTATION_ROTATE_90:
1293c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    extras = new Bundle(1);
1294c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    extras.putInt(EXTRA_ORIENTATION, 90);
1295c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    break;
1296c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                case ExifInterface.ORIENTATION_ROTATE_180:
1297c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    extras = new Bundle(1);
1298c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    extras.putInt(EXTRA_ORIENTATION, 180);
1299c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    break;
1300c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                case ExifInterface.ORIENTATION_ROTATE_270:
1301c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    extras = new Bundle(1);
1302c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    extras.putInt(EXTRA_ORIENTATION, 270);
1303c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                    break;
1304c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            }
1305c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1306c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            final long[] thumb = exif.getThumbnailRange();
1307c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            if (thumb != null) {
1308c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey                return new AssetFileDescriptor(pfd, thumb[0], thumb[1], extras);
1309c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey            }
1310c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        } catch (IOException e) {
1311c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        }
1312c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey
1313c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
1314c1c8f3f97d344a24bfddcb56a8be05e7e2fabe9eJeff Sharkey    }
13159ecfee03fa188aebfbd9778b4e020323903495eeJeff Sharkey}
1316