Separate root and document management.

Two hidden intents for managing roots and documents, used to support
Downloads UI.  Touching an item tries launching as MANAGE_DOCUMENT
first before falling back to VIEW.  Provide MIME type for roots.

Bug: 10446265, 10531347, 10599641
Change-Id: Ia5584bd6ce3e5a9b0048e8caf1447e3053664413
diff --git a/api/current.txt b/api/current.txt
index 8366333..fa29b8f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20747,6 +20747,7 @@
     method public static android.net.Uri buildChildDocumentsUri(java.lang.String, java.lang.String);
     method public static android.net.Uri buildDocumentUri(java.lang.String, 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 createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 13b6cfb..5333a25 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -72,7 +72,9 @@
     public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER";
 
     /** {@hide} */
-    public static final String ACTION_MANAGE_DOCUMENTS = "android.provider.action.MANAGE_DOCUMENTS";
+    public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
+    /** {@hide} */
+    public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
 
     /**
      * Constants related to a document, including {@link Cursor} columns names
@@ -346,6 +348,9 @@
          */
         public static final String COLUMN_MIME_TYPES = "mime_types";
 
+        /** {@hide} */
+        public static final String MIME_TYPE_ITEM = "vnd.android.document/root";
+
         /**
          * Type of root that represents a storage service, such as a cloud-based
          * service.
@@ -462,6 +467,17 @@
     }
 
     /**
+     * Build Uri representing the given {@link Root#COLUMN_ROOT_ID} in a
+     * document provider.
+     *
+     * @see #getRootId(Uri)
+     */
+    public static Uri buildRootUri(String authority, String rootId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority).appendPath(PATH_ROOT).appendPath(rootId).build();
+    }
+
+    /**
      * Build Uri representing the recently modified documents of a specific
      * root. When queried, a provider will return zero or more rows with columns
      * defined by {@link Document}.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 07cb2a9..bdfb776 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -75,11 +75,12 @@
 public abstract class DocumentsProvider extends ContentProvider {
     private static final String TAG = "DocumentsProvider";
 
-    private static final int MATCH_ROOT = 1;
-    private static final int MATCH_RECENT = 2;
-    private static final int MATCH_DOCUMENT = 3;
-    private static final int MATCH_CHILDREN = 4;
-    private static final int MATCH_SEARCH = 5;
+    private static final int MATCH_ROOTS = 1;
+    private static final int MATCH_ROOT = 2;
+    private static final int MATCH_RECENT = 3;
+    private static final int MATCH_DOCUMENT = 4;
+    private static final int MATCH_CHILDREN = 5;
+    private static final int MATCH_SEARCH = 6;
 
     private String mAuthority;
 
@@ -93,7 +94,8 @@
         mAuthority = info.authority;
 
         mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-        mMatcher.addURI(mAuthority, "root", MATCH_ROOT);
+        mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
+        mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
         mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
         mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
         mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
@@ -256,7 +258,7 @@
             String[] selectionArgs, String sortOrder) {
         try {
             switch (mMatcher.match(uri)) {
-                case MATCH_ROOT:
+                case MATCH_ROOTS:
                     return queryRoots(projection);
                 case MATCH_RECENT:
                     return queryRecentDocuments(getRootId(uri), projection);
@@ -285,6 +287,8 @@
     public final String getType(Uri uri) {
         try {
             switch (mMatcher.match(uri)) {
+                case MATCH_ROOT:
+                    return DocumentsContract.Root.MIME_TYPE_ITEM;
                 case MATCH_DOCUMENT:
                     return getDocumentType(getDocumentId(uri));
                 default:
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 45e2650..4c91bd3 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -30,11 +30,10 @@
                 <category android:name="android.intent.category.OPENABLE" />
                 <data android:mimeType="*/*" />
             </intent-filter>
-            <!-- data expected to point at existing root to manage -->
             <intent-filter>
-                <action android:name="android.provider.action.MANAGE_DOCUMENTS" />
+                <action android:name="android.provider.action.MANAGE_ROOT" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.document/directory" />
+                <data android:mimeType="vnd.android.document/root" />
             </intent-filter>
         </activity>
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 4da6567..8715055 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -145,7 +145,7 @@
             mState.action = ACTION_CREATE;
         } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
             mState.action = ACTION_GET_CONTENT;
-        } else if (DocumentsContract.ACTION_MANAGE_DOCUMENTS.equals(action)) {
+        } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
             mState.action = ACTION_MANAGE;
         }
 
@@ -171,12 +171,13 @@
         }
 
         if (mState.action == ACTION_MANAGE) {
-            final Uri rootUri = intent.getData();
-            final RootInfo root = mRoots.findRoot(rootUri);
+            final Uri uri = intent.getData();
+            final String rootId = DocumentsContract.getRootId(uri);
+            final RootInfo root = mRoots.getRoot(uri.getAuthority(), rootId);
             if (root != null) {
                 onRootPicked(root, true);
             } else {
-                Log.w(TAG, "Failed to find root: " + rootUri);
+                Log.w(TAG, "Failed to find root: " + uri);
                 finish();
             }
 
@@ -626,14 +627,24 @@
             // Replace selected file
             SaveFragment.get(fm).setReplaceTarget(doc);
         } else if (mState.action == ACTION_MANAGE) {
-            // Open the document
-            final Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            intent.setData(doc.uri);
+            // First try managing the document; we expect manager to filter
+            // based on authority, so we don't grant.
+            final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
+            manage.setData(doc.uri);
+
             try {
-                startActivity(intent);
+                startActivity(manage);
             } catch (ActivityNotFoundException ex) {
-                Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+                // Fall back to viewing
+                final Intent view = new Intent(Intent.ACTION_VIEW);
+                view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                view.setData(doc.uri);
+
+                try {
+                    startActivity(view);
+                } catch (ActivityNotFoundException ex2) {
+                    Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+                }
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
index c65464a..85d0988 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
@@ -16,13 +16,9 @@
 
 package com.android.documentsui;
 
-import android.util.Log;
-
 import com.android.documentsui.model.DocumentInfo;
 import com.android.internal.util.Predicate;
 
-import java.util.Arrays;
-
 public class MimePredicate implements Predicate<DocumentInfo> {
     private final String[] mFilters;