Merge "Filter roots based on incoming request." into klp-dev
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index c24341e..5b23ca5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -191,7 +191,9 @@
                                 authority, docId, query);
                         return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
                     case TYPE_RECENT_OPEN:
-                        return new RecentLoader(context);
+                        final RootsCache roots = DocumentsApplication.getRootsCache(context);
+                        final List<RootInfo> matchingRoots = roots.getMatchingRoots(state);
+                        return new RecentLoader(context, matchingRoots);
                     default:
                         throw new IllegalStateException("Unknown type " + mType);
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index da790cc..f569f5a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -161,6 +161,7 @@
         }
 
         mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+        mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this);
 
         if (mState.action == ACTION_MANAGE) {
             mState.sortOrder = SORT_ORDER_LAST_MODIFIED;
@@ -701,6 +702,7 @@
         public boolean allowMultiple = false;
         public boolean showSize = false;
         public boolean localOnly = false;
+        public boolean showAdvanced = false;
 
         /** Current user navigation stack; empty implies recents. */
         public DocumentStack stack = new DocumentStack();
@@ -733,6 +735,7 @@
             out.writeInt(allowMultiple ? 1 : 0);
             out.writeInt(showSize ? 1 : 0);
             out.writeInt(localOnly ? 1 : 0);
+            out.writeInt(showAdvanced ? 1 : 0);
             DurableUtils.writeToParcel(out, stack);
             out.writeString(currentSearch);
         }
@@ -748,6 +751,7 @@
                 state.allowMultiple = in.readInt() != 0;
                 state.showSize = in.readInt() != 0;
                 state.localOnly = in.readInt() != 0;
+                state.showAdvanced = in.readInt() != 0;
                 DurableUtils.readFromParcel(in, state.stack);
                 state.currentSearch = in.readString();
                 return state;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index 5f6fd13..756a297 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -78,6 +78,8 @@
         return executor;
     }
 
+    private final List<RootInfo> mRoots;
+
     private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap();
 
     private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED;
@@ -133,8 +135,9 @@
         }
     }
 
-    public RecentLoader(Context context) {
+    public RecentLoader(Context context, List<RootInfo> roots) {
         super(context);
+        mRoots = roots;
     }
 
     @Override
@@ -143,8 +146,7 @@
             // First time through we kick off all the recent tasks, and wait
             // around to see if everyone finishes quickly.
 
-            final RootsCache roots = DocumentsApplication.getRootsCache(getContext());
-            for (RootInfo root : roots.getRoots()) {
+            for (RootInfo root : mRoots) {
                 if ((root.flags & Root.FLAG_SUPPORTS_RECENTS) != 0) {
                     final RecentTask task = new RecentTask(root.authority, root.rootId);
                     mTasks.put(root, task);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index ac3b740..0b10f19 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -33,6 +33,7 @@
 import android.provider.DocumentsContract.Root;
 import android.util.Log;
 
+import com.android.documentsui.DocumentsActivity.State;
 import com.android.documentsui.model.RootInfo;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Objects;
@@ -40,6 +41,7 @@
 
 import libcore.io.IoUtils;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -75,6 +77,7 @@
             final RootInfo root = new RootInfo();
             root.rootType = Root.ROOT_TYPE_SHORTCUT;
             root.icon = R.drawable.ic_dir;
+            root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE;
             root.title = mContext.getString(R.string.root_recent);
             root.availableBytes = -1;
 
@@ -150,6 +153,60 @@
         return mRoots;
     }
 
+    /**
+     * Flags that declare explicit content types.
+     */
+    private static final int FLAGS_CONTENT_MASK = Root.FLAG_PROVIDES_IMAGES
+            | Root.FLAG_PROVIDES_AUDIO | Root.FLAG_PROVIDES_VIDEO;
+
+    @GuardedBy("ActivityThread")
+    public List<RootInfo> getMatchingRoots(State state) {
+
+        // Determine acceptable content flags
+        int includeFlags = 0;
+        for (String acceptMime : state.acceptMimes) {
+            final String[] type = acceptMime.split("/");
+            if (type.length != 2) continue;
+
+            if ("image".equals(type[0])) {
+                includeFlags |= Root.FLAG_PROVIDES_IMAGES;
+            } else if ("audio".equals(type[0])) {
+                includeFlags |= Root.FLAG_PROVIDES_AUDIO;
+            } else if ("video".equals(type[0])) {
+                includeFlags |= Root.FLAG_PROVIDES_VIDEO;
+            } else if ("*".equals(type[0])) {
+                includeFlags |= Root.FLAG_PROVIDES_IMAGES | Root.FLAG_PROVIDES_AUDIO
+                        | Root.FLAG_PROVIDES_VIDEO;
+            }
+        }
+
+        ArrayList<RootInfo> matching = Lists.newArrayList();
+        for (RootInfo root : mRoots) {
+            final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0;
+            final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0;
+            final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0;
+
+            // Exclude read-only devices when creating
+            if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
+            // Exclude advanced devices when not requested
+            if (!state.showAdvanced && advanced) continue;
+            // Exclude non-local devices when local only
+            if (state.localOnly && !localOnly) continue;
+
+            if ((root.flags & FLAGS_CONTENT_MASK) != 0) {
+                // This root offers specific content, so only include if the
+                // caller asked for that content type.
+                if ((root.flags & includeFlags) == 0) {
+                    // Sorry, no overlap.
+                    continue;
+                }
+            }
+
+            matching.add(root);
+        }
+        return matching;
+    }
+
     @GuardedBy("ActivityThread")
     public static Drawable resolveDocumentIcon(Context context, String mimeType) {
         if (Document.MIME_TYPE_DIR.equals(mimeType)) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 3102b88..ef3a31d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -16,8 +16,6 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.DocumentsActivity.TAG;
-
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -28,7 +26,6 @@
 import android.os.Bundle;
 import android.provider.DocumentsContract.Root;
 import android.text.format.Formatter;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,6 +36,7 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import com.android.documentsui.DocumentsActivity.State;
 import com.android.documentsui.SectionedListAdapter.SectionAdapter;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
@@ -76,24 +74,31 @@
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         final Context context = inflater.getContext();
-        final RootsCache roots = DocumentsApplication.getRootsCache(context);
 
         final View view = inflater.inflate(R.layout.fragment_roots, container, false);
         mList = (ListView) view.findViewById(android.R.id.list);
         mList.setOnItemClickListener(mItemListener);
 
-        final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
-        mAdapter = new SectionedRootsAdapter(context, roots.getRoots(), includeApps);
-
         return view;
     }
 
     @Override
     public void onStart() {
         super.onStart();
+        updateRootsAdapter();
+    }
 
+    private void updateRootsAdapter() {
         final Context context = getActivity();
-        mAdapter.updateVisible(SettingsActivity.getDisplayAdvancedDevices(context));
+
+        final State state = ((DocumentsActivity) context).getDisplayState();
+        state.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(context);
+
+        final RootsCache roots = DocumentsApplication.getRootsCache(context);
+        final List<RootInfo> matchingRoots = roots.getMatchingRoots(state);
+        final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
+
+        mAdapter = new SectionedRootsAdapter(context, matchingRoots, includeApps);
         mList.setAdapter(mAdapter);
     }
 
@@ -211,18 +216,15 @@
         private final RootsAdapter mServices;
         private final RootsAdapter mShortcuts;
         private final RootsAdapter mDevices;
-        private final RootsAdapter mDevicesAdvanced;
         private final AppsAdapter mApps;
 
         public SectionedRootsAdapter(Context context, List<RootInfo> roots, Intent includeApps) {
             mServices = new RootsAdapter(context, R.string.root_type_service);
             mShortcuts = new RootsAdapter(context, R.string.root_type_shortcut);
             mDevices = new RootsAdapter(context, R.string.root_type_device);
-            mDevicesAdvanced = new RootsAdapter(context, R.string.root_type_device);
             mApps = new AppsAdapter(context);
 
             for (RootInfo root : roots) {
-                Log.d(TAG, "Found rootType=" + root.rootType);
                 switch (root.rootType) {
                     case Root.ROOT_TYPE_SERVICE:
                         mServices.add(root);
@@ -231,10 +233,7 @@
                         mShortcuts.add(root);
                         break;
                     case Root.ROOT_TYPE_DEVICE:
-                        mDevicesAdvanced.add(root);
-                        if ((root.flags & Root.FLAG_ADVANCED) == 0) {
-                            mDevices.add(root);
-                        }
+                        mDevices.add(root);
                         break;
                 }
             }
@@ -256,23 +255,16 @@
             mServices.sort(comp);
             mShortcuts.sort(comp);
             mDevices.sort(comp);
-            mDevicesAdvanced.sort(comp);
-        }
 
-        public void updateVisible(boolean showAdvanced) {
-            clearSections();
             if (mServices.getCount() > 0) {
                 addSection(mServices);
             }
             if (mShortcuts.getCount() > 0) {
                 addSection(mShortcuts);
             }
-
-            final RootsAdapter devices = showAdvanced ? mDevicesAdvanced : mDevices;
-            if (devices.getCount() > 0) {
-                addSection(devices);
+            if (mDevices.getCount() > 0) {
+                addSection(mDevices);
             }
-
             if (mApps.getCount() > 0) {
                 addSection(mApps);
             }
@@ -282,6 +274,12 @@
     public static class RootComparator implements Comparator<RootInfo> {
         @Override
         public int compare(RootInfo lhs, RootInfo rhs) {
+            if (lhs.authority == null) {
+                return -1;
+            } else if (rhs.authority == null) {
+                return 1;
+            }
+
             final int score = DocumentInfo.compareToIgnoreCaseNullable(lhs.title, rhs.title);
             if (score != 0) {
                 return score;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
index f6548e8..2405cb5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -32,7 +32,6 @@
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
-import java.io.IOException;
 import java.io.InputStream;
 
 public class TestActivity extends Activity {
@@ -50,8 +49,11 @@
         view.setOrientation(LinearLayout.VERTICAL);
 
         final CheckBox multiple = new CheckBox(context);
-        multiple.setText("ALLOW_MULTIPLE");
+        multiple.setText("\nALLOW_MULTIPLE\n");
         view.addView(multiple);
+        final CheckBox localOnly = new CheckBox(context);
+        localOnly.setText("\nLOCAL_ONLY\n");
+        view.addView(localOnly);
 
         Button button;
         button = new Button(context);
@@ -65,6 +67,9 @@
                 if (multiple.isChecked()) {
                     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                 }
+                if (localOnly.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+                }
                 startActivityForResult(intent, 42);
             }
         });
@@ -81,6 +86,28 @@
                 if (multiple.isChecked()) {
                     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                 }
+                if (localOnly.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+                }
+                startActivityForResult(intent, 42);
+            }
+        });
+        view.addView(button);
+
+        button = new Button(context);
+        button.setText("OPEN_DOC audio/ogg");
+        button.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+                intent.addCategory(Intent.CATEGORY_OPENABLE);
+                intent.setType("audio/ogg");
+                if (multiple.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+                }
+                if (localOnly.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+                }
                 startActivityForResult(intent, 42);
             }
         });
@@ -99,6 +126,9 @@
                 if (multiple.isChecked()) {
                     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                 }
+                if (localOnly.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+                }
                 startActivityForResult(intent, 42);
             }
         });
@@ -113,6 +143,9 @@
                 intent.addCategory(Intent.CATEGORY_OPENABLE);
                 intent.setType("text/plain");
                 intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt");
+                if (localOnly.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+                }
                 startActivityForResult(intent, 42);
             }
         });
@@ -129,6 +162,9 @@
                 if (multiple.isChecked()) {
                     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                 }
+                if (localOnly.isChecked()) {
+                    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+                }
                 startActivityForResult(Intent.createChooser(intent, "Kittens!"), 42);
             }
         });
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index de8c29a..bbe3b45 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -87,9 +87,7 @@
             final RootInfo root = new RootInfo();
             root.rootId = "primary";
             root.rootType = Root.ROOT_TYPE_DEVICE;
-            root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
-                    | Root.FLAG_PROVIDES_AUDIO | Root.FLAG_PROVIDES_VIDEO
-                    | Root.FLAG_PROVIDES_IMAGES;
+            root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED;
             root.icon = R.drawable.ic_pdf;
             root.title = getContext().getString(R.string.root_internal_storage);
             root.docId = getDocIdForFile(path);