diff options
9 files changed, 155 insertions, 133 deletions
diff --git a/api/current.txt b/api/current.txt index 791d037..53f361c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7388,6 +7388,7 @@ package android.content { field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED"; field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"; field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT"; + field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; @@ -7402,7 +7403,6 @@ package android.content { field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE"; field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK"; field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY"; - field public static final java.lang.String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY"; field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED"; field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY"; @@ -23811,21 +23811,21 @@ package android.provider { public final class DocumentsContract { method public static android.net.Uri buildChildDocumentsUri(java.lang.String, java.lang.String); - method public static android.net.Uri buildChildDocumentsViaUri(android.net.Uri, java.lang.String); + method public static android.net.Uri buildChildDocumentsUriUsingTree(android.net.Uri, java.lang.String); method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String); - method public static android.net.Uri buildDocumentViaUri(android.net.Uri, java.lang.String); + method public static android.net.Uri buildDocumentUriUsingTree(android.net.Uri, java.lang.String); method public static android.net.Uri buildRecentDocumentsUri(java.lang.String, java.lang.String); method public static android.net.Uri buildRootUri(java.lang.String, java.lang.String); method public static android.net.Uri buildRootsUri(java.lang.String); method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String); - method public static android.net.Uri buildViaUri(java.lang.String, java.lang.String); + method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String); method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String); method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri); method public static java.lang.String getDocumentId(android.net.Uri); method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal); method public static java.lang.String getRootId(android.net.Uri); method public static java.lang.String getSearchDocumentsQuery(android.net.Uri); - method public static java.lang.String getViaDocumentId(android.net.Uri); + method public static java.lang.String getTreeDocumentId(android.net.Uri); method public static boolean isDocumentUri(android.content.Context, android.net.Uri); method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String); field public static final java.lang.String EXTRA_ERROR = "error"; @@ -23864,7 +23864,7 @@ package android.provider { field public static final java.lang.String COLUMN_TITLE = "title"; field public static final int FLAG_LOCAL_ONLY = 2; // 0x2 field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 - field public static final int FLAG_SUPPORTS_DIR_SELECTION = 16; // 0x10 + field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10 field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4 field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8 } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index fad3851..cd0de12 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2731,6 +2731,7 @@ public class Intent implements Parcelable, Cloneable { * returned in {@link #getClipData()}. * * @see DocumentsContract + * @see #ACTION_OPEN_DOCUMENT_TREE * @see #ACTION_CREATE_DOCUMENT * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION */ @@ -2765,28 +2766,30 @@ public class Intent implements Parcelable, Cloneable { * * @see DocumentsContract * @see #ACTION_OPEN_DOCUMENT + * @see #ACTION_OPEN_DOCUMENT_TREE * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; /** - * Activity Action: Allow the user to pick a directory. When invoked, the - * system will display the various {@link DocumentsProvider} instances - * installed on the device, letting the user navigate through them. Apps can - * fully manage documents within the returned directory. + * Activity Action: Allow the user to pick a directory subtree. When + * invoked, the system will display the various {@link DocumentsProvider} + * instances installed on the device, letting the user navigate through + * them. Apps can fully manage documents within the returned directory. * <p> * To gain access to descendant (child, grandchild, etc) documents, use - * {@link DocumentsContract#buildDocumentViaUri(Uri, String)} and - * {@link DocumentsContract#buildChildDocumentsViaUri(Uri, String)} using - * the returned directory URI. + * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)} and + * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} + * with the returned URI. * <p> - * Output: The URI representing the selected directory. + * Output: The URI representing the selected directory tree. * * @see DocumentsContract */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY"; + public static final String + ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; // --------------------------------------------------------------------- // --------------------------------------------------------------------- @@ -3365,8 +3368,8 @@ public class Intent implements Parcelable, Cloneable { * * @see #ACTION_GET_CONTENT * @see #ACTION_OPEN_DOCUMENT + * @see #ACTION_OPEN_DOCUMENT_TREE * @see #ACTION_CREATE_DOCUMENT - * @see #ACTION_PICK_DIRECTORY */ public static final String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY"; diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 6b8e2de..327fe4a 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -60,7 +60,8 @@ import java.util.List; * <p> * All client apps must hold a valid URI permission grant to access documents, * typically issued when a user makes a selection through - * {@link Intent#ACTION_OPEN_DOCUMENT} or {@link Intent#ACTION_CREATE_DOCUMENT}. + * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT}, + * or {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. * * @see DocumentsProvider */ @@ -73,8 +74,8 @@ public final class DocumentsContract { // content://com.example/root/sdcard/search/?query=pony // content://com.example/document/12/ // content://com.example/document/12/children/ - // content://com.example/via/12/document/24/ - // content://com.example/via/12/document/24/children/ + // content://com.example/tree/12/document/24/ + // content://com.example/tree/12/document/24/children/ private DocumentsContract() { } @@ -441,12 +442,13 @@ public final class DocumentsContract { public static final int FLAG_SUPPORTS_SEARCH = 1 << 3; /** - * Flag indicating that this root supports directory selection. + * Flag indicating that this root supports testing parent child + * relationships. * * @see #COLUMN_FLAGS * @see DocumentsProvider#isChildDocument(String, String) */ - public static final int FLAG_SUPPORTS_DIR_SELECTION = 1 << 4; + public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4; /** * Flag indicating that this root is currently empty. This may be used @@ -518,7 +520,7 @@ public final class DocumentsContract { private static final String PATH_DOCUMENT = "document"; private static final String PATH_CHILDREN = "children"; private static final String PATH_SEARCH = "search"; - private static final String PATH_VIA = "via"; + private static final String PATH_TREE = "tree"; private static final String PARAM_QUERY = "query"; private static final String PARAM_MANAGE = "manage"; @@ -564,17 +566,17 @@ public final class DocumentsContract { * Build URI representing access to descendant documents of the given * {@link Document#COLUMN_DOCUMENT_ID}. * - * @see #getViaDocumentId(Uri) + * @see #getTreeDocumentId(Uri) */ - public static Uri buildViaUri(String authority, String documentId) { + public static Uri buildTreeDocumentUri(String authority, String documentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) - .appendPath(PATH_VIA).appendPath(documentId).build(); + .appendPath(PATH_TREE).appendPath(documentId).build(); } /** - * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a - * document provider. When queried, a provider will return a single row with - * columns defined by {@link Document}. + * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in + * a document provider. When queried, a provider will return a single row + * with columns defined by {@link Document}. * * @see DocumentsProvider#queryDocument(String, String[]) * @see #getDocumentId(Uri) @@ -585,42 +587,46 @@ public final class DocumentsContract { } /** - * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a - * document provider. Instead of directly accessing the target document, - * gain access via another document. The target document must be a - * descendant (child, grandchild, etc) of the via document. + * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in + * a document provider. When queried, a provider will return a single row + * with columns defined by {@link Document}. + * <p> + * However, instead of directly accessing the target document, the returned + * URI will leverage access granted through a subtree URI, typically + * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target document + * must be a descendant (child, grandchild, etc) of the subtree. * <p> * This is typically used to access documents under a user-selected - * directory, since it doesn't require the user to separately confirm each - * new document access. + * directory tree, since it doesn't require the user to separately confirm + * each new document access. * - * @param viaUri a related document (directory) that the caller is - * leveraging to gain access to the target document. The target - * document must be a descendant of this directory. + * @param treeUri the subtree to leverage to gain access to the target + * document. The target directory must be a descendant of this + * subtree. * @param documentId the target document, which the caller may not have * direct access to. - * @see Intent#ACTION_PICK_DIRECTORY + * @see Intent#ACTION_OPEN_DOCUMENT_TREE * @see DocumentsProvider#isChildDocument(String, String) * @see #buildDocumentUri(String, String) */ - public static Uri buildDocumentViaUri(Uri viaUri, String documentId) { + public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(viaUri.getAuthority()).appendPath(PATH_VIA) - .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT) + .authority(treeUri.getAuthority()).appendPath(PATH_TREE) + .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT) .appendPath(documentId).build(); } /** {@hide} */ - public static Uri buildDocumentMaybeViaUri(Uri baseUri, String documentId) { - if (isViaUri(baseUri)) { - return buildDocumentViaUri(baseUri, documentId); + public static Uri buildDocumentUriMaybeUsingTree(Uri baseUri, String documentId) { + if (isTreeUri(baseUri)) { + return buildDocumentUriUsingTree(baseUri, documentId); } else { return buildDocumentUri(baseUri.getAuthority(), documentId); } } /** - * Build URI representing the children of the given directory in a document + * Build URI representing the children of the target directory in a document * provider. When queried, a provider will return zero or more rows with * columns defined by {@link Document}. * @@ -637,28 +643,33 @@ public final class DocumentsContract { } /** - * Build URI representing the children of the given directory in a document - * provider. Instead of directly accessing the target document, gain access - * via another document. The target document must be a descendant (child, - * grandchild, etc) of the via document. + * Build URI representing the children of the target directory in a document + * provider. When queried, a provider will return zero or more rows with + * columns defined by {@link Document}. + * <p> + * However, instead of directly accessing the target directory, the returned + * URI will leverage access granted through a subtree URI, typically + * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target + * directory must be a descendant (child, grandchild, etc) of the subtree. * <p> * This is typically used to access documents under a user-selected - * directory, since it doesn't require the user to separately confirm each - * new document access. + * directory tree, since it doesn't require the user to separately confirm + * each new document access. * - * @param viaUri a related document (directory) that the caller is - * leveraging to gain access to the target document. The target - * document must be a descendant of this directory. - * @param parentDocumentId the target document, which the caller may not - * have direct access to. - * @see Intent#ACTION_PICK_DIRECTORY + * @param treeUri the subtree to leverage to gain access to the target + * document. The target directory must be a descendant of this + * subtree. + * @param parentDocumentId the document to return children for, which the + * caller may not have direct access to, and which must be a + * directory with MIME type of {@link Document#MIME_TYPE_DIR}. + * @see Intent#ACTION_OPEN_DOCUMENT_TREE * @see DocumentsProvider#isChildDocument(String, String) * @see #buildChildDocumentsUri(String, String) */ - public static Uri buildChildDocumentsViaUri(Uri viaUri, String parentDocumentId) { + public static Uri buildChildDocumentsUriUsingTree(Uri treeUri, String parentDocumentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(viaUri.getAuthority()).appendPath(PATH_VIA) - .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT) + .authority(treeUri.getAuthority()).appendPath(PATH_TREE) + .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT) .appendPath(parentDocumentId).appendPath(PATH_CHILDREN).build(); } @@ -683,21 +694,24 @@ public final class DocumentsContract { * {@link DocumentsProvider}. * * @see #buildDocumentUri(String, String) - * @see #buildDocumentViaUri(Uri, String) + * @see #buildDocumentUriUsingTree(Uri, String) */ public static boolean isDocumentUri(Context context, Uri uri) { final List<String> paths = uri.getPathSegments(); - if (paths.size() >= 2 - && (PATH_DOCUMENT.equals(paths.get(0)) || PATH_VIA.equals(paths.get(0)))) { + if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) { + return isDocumentsProvider(context, uri.getAuthority()); + } + if (paths.size() == 4 && PATH_TREE.equals(paths.get(0)) + && PATH_DOCUMENT.equals(paths.get(2))) { return isDocumentsProvider(context, uri.getAuthority()); } return false; } /** {@hide} */ - public static boolean isViaUri(Uri uri) { + public static boolean isTreeUri(Uri uri) { final List<String> paths = uri.getPathSegments(); - return (paths.size() >= 2 && PATH_VIA.equals(paths.get(0))); + return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))); } private static boolean isDocumentsProvider(Context context, String authority) { @@ -733,7 +747,7 @@ public final class DocumentsContract { if (paths.size() >= 2 && PATH_DOCUMENT.equals(paths.get(0))) { return paths.get(1); } - if (paths.size() >= 4 && PATH_VIA.equals(paths.get(0)) + if (paths.size() >= 4 && PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2))) { return paths.get(3); } @@ -742,12 +756,10 @@ public final class DocumentsContract { /** * Extract the via {@link Document#COLUMN_DOCUMENT_ID} from the given URI. - * - * @see #isViaUri(Uri) */ - public static String getViaDocumentId(Uri documentUri) { + public static String getTreeDocumentId(Uri documentUri) { final List<String> paths = documentUri.getPathSegments(); - if (paths.size() >= 2 && PATH_VIA.equals(paths.get(0))) { + if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) { return paths.get(1); } throw new IllegalArgumentException("Invalid URI: " + documentUri); diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 066b4aa..021fff4 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -20,10 +20,14 @@ import static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE; import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT; +import static android.provider.DocumentsContract.buildDocumentUri; +import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree; +import static android.provider.DocumentsContract.buildTreeDocumentUri; import static android.provider.DocumentsContract.getDocumentId; import static android.provider.DocumentsContract.getRootId; import static android.provider.DocumentsContract.getSearchDocumentsQuery; -import static android.provider.DocumentsContract.isViaUri; +import static android.provider.DocumentsContract.getTreeDocumentId; +import static android.provider.DocumentsContract.isTreeUri; import android.content.ContentProvider; import android.content.ContentResolver; @@ -117,6 +121,7 @@ import java.util.Objects; * </p> * * @see Intent#ACTION_OPEN_DOCUMENT + * @see Intent#ACTION_OPEN_DOCUMENT_TREE * @see Intent#ACTION_CREATE_DOCUMENT */ public abstract class DocumentsProvider extends ContentProvider { @@ -128,8 +133,8 @@ public abstract class DocumentsProvider extends ContentProvider { private static final int MATCH_SEARCH = 4; private static final int MATCH_DOCUMENT = 5; private static final int MATCH_CHILDREN = 6; - private static final int MATCH_DOCUMENT_VIA = 7; - private static final int MATCH_CHILDREN_VIA = 8; + private static final int MATCH_DOCUMENT_TREE = 7; + private static final int MATCH_CHILDREN_TREE = 8; private String mAuthority; @@ -149,8 +154,8 @@ public abstract class DocumentsProvider extends ContentProvider { mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH); mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT); mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN); - mMatcher.addURI(mAuthority, "via/*/document/*", MATCH_DOCUMENT_VIA); - mMatcher.addURI(mAuthority, "via/*/document/*/children", MATCH_CHILDREN_VIA); + mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE); + mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE); // Sanity check our setup if (!info.exported) { @@ -169,23 +174,24 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Test if a document is descendant (child, grandchild, etc) from the given - * parent. Providers must override this to support directory selection. You - * should avoid making network requests to keep this request fast. + * parent. For example, providers must implement this to support + * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. You should avoid making network + * requests to keep this request fast. * * @param parentDocumentId parent to verify against. * @param documentId child to verify. * @return if given document is a descendant of the given parent. - * @see DocumentsContract.Root#FLAG_SUPPORTS_DIR_SELECTION + * @see DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD */ public boolean isChildDocument(String parentDocumentId, String documentId) { return false; } /** {@hide} */ - private void enforceVia(Uri documentUri) { - if (DocumentsContract.isViaUri(documentUri)) { - final String parent = DocumentsContract.getViaDocumentId(documentUri); - final String child = DocumentsContract.getDocumentId(documentUri); + private void enforceTree(Uri documentUri) { + if (isTreeUri(documentUri)) { + final String parent = getTreeDocumentId(documentUri); + final String child = getDocumentId(documentUri); if (Objects.equals(parent, child)) { return; } @@ -479,12 +485,12 @@ public abstract class DocumentsProvider extends ContentProvider { return querySearchDocuments( getRootId(uri), getSearchDocumentsQuery(uri), projection); case MATCH_DOCUMENT: - case MATCH_DOCUMENT_VIA: - enforceVia(uri); + case MATCH_DOCUMENT_TREE: + enforceTree(uri); return queryDocument(getDocumentId(uri), projection); case MATCH_CHILDREN: - case MATCH_CHILDREN_VIA: - enforceVia(uri); + case MATCH_CHILDREN_TREE: + enforceTree(uri); if (DocumentsContract.isManageMode(uri)) { return queryChildDocumentsForManage( getDocumentId(uri), projection, sortOrder); @@ -512,8 +518,8 @@ public abstract class DocumentsProvider extends ContentProvider { case MATCH_ROOT: return DocumentsContract.Root.MIME_TYPE_ITEM; case MATCH_DOCUMENT: - case MATCH_DOCUMENT_VIA: - enforceVia(uri); + case MATCH_DOCUMENT_TREE: + enforceTree(uri); return getDocumentType(getDocumentId(uri)); default: return null; @@ -530,21 +536,20 @@ public abstract class DocumentsProvider extends ContentProvider { * call the superclass. If the superclass returns {@code null}, the subclass * may implement custom behavior. * <p> - * This is typically used to resolve a "via" URI into a concrete document + * This is typically used to resolve a subtree URI into a concrete document * reference, issuing a narrower single-document URI permission grant along * the way. * - * @see DocumentsContract#buildDocumentViaUri(Uri, String) + * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String) */ @Override public Uri canonicalize(Uri uri) { final Context context = getContext(); switch (mMatcher.match(uri)) { - case MATCH_DOCUMENT_VIA: - enforceVia(uri); + case MATCH_DOCUMENT_TREE: + enforceTree(uri); - final Uri narrowUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), - DocumentsContract.getDocumentId(uri)); + final Uri narrowUri = buildDocumentUri(uri.getAuthority(), getDocumentId(uri)); // Caller may only have prefix grant, so extend them a grant to // the narrow URI. @@ -628,7 +633,7 @@ public abstract class DocumentsProvider extends ContentProvider { throw new SecurityException( "Requested authority " + authority + " doesn't match provider " + mAuthority); } - enforceVia(documentUri); + enforceTree(documentUri); final Bundle out = new Bundle(); try { @@ -641,8 +646,8 @@ public abstract class DocumentsProvider extends ContentProvider { // No need to issue new grants here, since caller either has // manage permission or a prefix grant. We might generate a - // "via" style URI if that's how they called us. - final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(documentUri, + // tree style URI if that's how they called us. + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, newDocumentId); out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); @@ -653,12 +658,12 @@ public abstract class DocumentsProvider extends ContentProvider { final String newDocumentId = renameDocument(documentId, displayName); if (newDocumentId != null) { - final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri( - documentUri, newDocumentId); + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, + newDocumentId); // If caller came in with a narrow grant, issue them a // narrow grant for the newly renamed document. - if (!isViaUri(newDocumentUri)) { + if (!isTreeUri(newDocumentUri)) { final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, documentUri); context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); @@ -694,8 +699,8 @@ public abstract class DocumentsProvider extends ContentProvider { */ public final void revokeDocumentPermission(String documentId) { final Context context = getContext(); - context.revokeUriPermission(DocumentsContract.buildDocumentUri(mAuthority, documentId), ~0); - context.revokeUriPermission(DocumentsContract.buildViaUri(mAuthority, documentId), ~0); + context.revokeUriPermission(buildDocumentUri(mAuthority, documentId), ~0); + context.revokeUriPermission(buildTreeDocumentUri(mAuthority, documentId), ~0); } /** @@ -705,7 +710,7 @@ public abstract class DocumentsProvider extends ContentProvider { */ @Override public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); return openDocument(getDocumentId(uri), mode, null); } @@ -717,7 +722,7 @@ public abstract class DocumentsProvider extends ContentProvider { @Override public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); return openDocument(getDocumentId(uri), mode, signal); } @@ -730,7 +735,7 @@ public abstract class DocumentsProvider extends ContentProvider { @SuppressWarnings("resource") public final AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, null); return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; } @@ -744,7 +749,7 @@ public abstract class DocumentsProvider extends ContentProvider { @SuppressWarnings("resource") public final AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, signal); return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; } @@ -757,7 +762,7 @@ public abstract class DocumentsProvider extends ContentProvider { @Override public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); return openDocumentThumbnail(getDocumentId(uri), sizeHint, null); @@ -775,7 +780,7 @@ public abstract class DocumentsProvider extends ContentProvider { public final AssetFileDescriptor openTypedAssetFile( Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal); diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 159ee66..3861cc1 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -32,7 +32,7 @@ <data android:mimeType="*/*" /> </intent-filter> <intent-filter> - <action android:name="android.intent.action.PICK_DIRECTORY" /> + <action android:name="android.intent.action.OPEN_DOCUMENT_TREE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <intent-filter> diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 9f76991..d0b6a1d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -24,7 +24,7 @@ import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE; import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT; import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE; import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN; -import static com.android.documentsui.DocumentsActivity.State.ACTION_PICK_DIRECTORY; +import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN_TREE; import static com.android.documentsui.DocumentsActivity.State.MODE_GRID; import static com.android.documentsui.DocumentsActivity.State.MODE_LIST; @@ -203,7 +203,7 @@ public class DocumentsActivity extends Activity { final String mimeType = getIntent().getType(); final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE); SaveFragment.show(getFragmentManager(), mimeType, title); - } else if (mState.action == ACTION_PICK_DIRECTORY) { + } else if (mState.action == ACTION_OPEN_TREE) { PickFragment.show(getFragmentManager()); } @@ -213,7 +213,7 @@ public class DocumentsActivity extends Activity { moreApps.setPackage(null); RootsFragment.show(getFragmentManager(), moreApps); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE - || mState.action == ACTION_PICK_DIRECTORY) { + || mState.action == ACTION_OPEN_TREE) { RootsFragment.show(getFragmentManager(), null); } @@ -240,8 +240,8 @@ public class DocumentsActivity extends Activity { mState.action = ACTION_CREATE; } else if (Intent.ACTION_GET_CONTENT.equals(action)) { mState.action = ACTION_GET_CONTENT; - } else if (Intent.ACTION_PICK_DIRECTORY.equals(action)) { - mState.action = ACTION_PICK_DIRECTORY; + } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) { + mState.action = ACTION_OPEN_TREE; } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) { mState.action = ACTION_MANAGE; } @@ -441,7 +441,7 @@ public class DocumentsActivity extends Activity { actionBar.setIcon(new ColorDrawable()); if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT - || mState.action == ACTION_PICK_DIRECTORY) { + || mState.action == ACTION_OPEN_TREE) { actionBar.setTitle(R.string.title_open); } else if (mState.action == ACTION_CREATE) { actionBar.setTitle(R.string.title_save); @@ -583,7 +583,7 @@ public class DocumentsActivity extends Activity { sortSize.setVisible(mState.showSize); final boolean searchVisible; - if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) { + if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) { createDir.setVisible(cwd != null && cwd.isCreateSupported()); searchVisible = false; @@ -828,7 +828,7 @@ public class DocumentsActivity extends Activity { if (cwd == null) { // No directory means recents - if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) { + if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) { RecentsCreateFragment.show(fm); } else { DirectoryFragment.showRecentsOpen(fm, anim); @@ -857,7 +857,7 @@ public class DocumentsActivity extends Activity { } } - if (mState.action == ACTION_PICK_DIRECTORY) { + if (mState.action == ACTION_OPEN_TREE) { final PickFragment pick = PickFragment.get(fm); if (pick != null) { final CharSequence displayName = (mState.stack.size() <= 1) ? root.title @@ -1021,7 +1021,7 @@ public class DocumentsActivity extends Activity { } public void onPickRequested(DocumentInfo pickTarget) { - final Uri viaUri = DocumentsContract.buildViaUri(pickTarget.authority, + final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority, pickTarget.documentId); new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor()); } @@ -1031,7 +1031,7 @@ public class DocumentsActivity extends Activity { final ContentValues values = new ContentValues(); final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack); - if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) { + if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) { // Remember stack for last create values.clear(); values.put(RecentColumns.KEY, mState.stack.buildKey()); @@ -1064,7 +1064,7 @@ public class DocumentsActivity extends Activity { if (mState.action == ACTION_GET_CONTENT) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - } else if (mState.action == ACTION_PICK_DIRECTORY) { + } else if (mState.action == ACTION_OPEN_TREE) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION @@ -1202,7 +1202,7 @@ public class DocumentsActivity extends Activity { public static final int ACTION_OPEN = 1; public static final int ACTION_CREATE = 2; public static final int ACTION_GET_CONTENT = 3; - public static final int ACTION_PICK_DIRECTORY = 4; + public static final int ACTION_OPEN_TREE = 4; public static final int ACTION_MANAGE = 5; public static final int MODE_UNKNOWN = 0; diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java index a9e488a1..5112c92 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java @@ -77,13 +77,15 @@ public class PickFragment extends Fragment { public void setPickTarget(DocumentInfo pickTarget, CharSequence displayName) { mPickTarget = pickTarget; - if (mPickTarget != null) { - mContainer.setVisibility(View.VISIBLE); - final Locale locale = getResources().getConfiguration().locale; - final String raw = getString(R.string.menu_select).toUpperCase(locale); - mPick.setText(TextUtils.expandTemplate(raw, displayName)); - } else { - mContainer.setVisibility(View.GONE); + if (mContainer != null) { + if (mPickTarget != null) { + mContainer.setVisibility(View.VISIBLE); + final Locale locale = getResources().getConfiguration().locale; + final String raw = getString(R.string.menu_select).toUpperCase(locale); + mPick.setText(TextUtils.expandTemplate(raw, displayName)); + } else { + mContainer.setVisibility(View.GONE); + } } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index 933dbe0..caa7581 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -105,7 +105,7 @@ public class RootsCache { mRecentsRoot.rootId = null; mRecentsRoot.icon = R.drawable.ic_root_recent; mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE - | Root.FLAG_SUPPORTS_DIR_SELECTION; + | Root.FLAG_SUPPORTS_IS_CHILD; mRecentsRoot.title = mContext.getString(R.string.root_recent); mRecentsRoot.availableBytes = -1; @@ -350,7 +350,7 @@ public class RootsCache { final List<RootInfo> matching = Lists.newArrayList(); for (RootInfo root : roots) { final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0; - final boolean supportsDir = (root.flags & Root.FLAG_SUPPORTS_DIR_SELECTION) != 0; + final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0; final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0; final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0; final boolean empty = (root.flags & Root.FLAG_EMPTY) != 0; @@ -358,7 +358,7 @@ public class RootsCache { // Exclude read-only devices when creating if (state.action == State.ACTION_CREATE && !supportsCreate) continue; // Exclude roots that don't support directory picking - if (state.action == State.ACTION_PICK_DIRECTORY && !supportsDir) continue; + if (state.action == State.ACTION_OPEN_TREE && !supportsIsChild) continue; // Exclude advanced devices when not requested if (!state.showAdvanced && advanced) continue; // Exclude non-local devices when local only diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index d388ab7..9473eb9 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -145,7 +145,7 @@ public class ExternalStorageProvider extends DocumentsProvider { final RootInfo root = new RootInfo(); root.rootId = rootId; root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED - | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_DIR_SELECTION; + | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD; if (ROOT_ID_PRIMARY_EMULATED.equals(rootId)) { root.title = getContext().getString(R.string.root_internal_storage); } else { |