am cc637d9a: Merge "New roots UX, async, performance, docs." into klp-dev

* commit 'cc637d9ae5920ac2f685090f595dc1e1643d71cc':
  New roots UX, async, performance, docs.
diff --git a/api/current.txt b/api/current.txt
index 02eacfe..37881e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20988,7 +20988,6 @@
     field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
     field public static final java.lang.String COLUMN_SIZE = "_size";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
-    field public static final int FLAG_DIR_HIDE_GRID_TITLES = 64; // 0x40
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -21005,18 +21004,12 @@
     field public static final java.lang.String COLUMN_ICON = "icon";
     field public static final java.lang.String COLUMN_MIME_TYPES = "mime_types";
     field public static final java.lang.String COLUMN_ROOT_ID = "root_id";
-    field public static final java.lang.String COLUMN_ROOT_TYPE = "root_type";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
     field public static final java.lang.String COLUMN_TITLE = "title";
-    field public static final int FLAG_ADVANCED = 4; // 0x4
-    field public static final int FLAG_EMPTY = 32; // 0x20
     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_RECENTS = 8; // 0x8
-    field public static final int FLAG_SUPPORTS_SEARCH = 16; // 0x10
-    field public static final int ROOT_TYPE_DEVICE = 3; // 0x3
-    field public static final int ROOT_TYPE_SERVICE = 1; // 0x1
-    field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2
+    field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
+    field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
   }
 
   public abstract class DocumentsProvider extends android.content.ContentProvider {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4c9af19..85ec803 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -99,7 +99,7 @@
         /**
          * Unique ID of a document. This ID is both provided by and interpreted
          * by a {@link DocumentsProvider}, and should be treated as an opaque
-         * value by client applications.
+         * value by client applications. This column is required.
          * <p>
          * Each document must have a unique ID within a provider, but that
          * single document may be included as a child of multiple directories.
@@ -117,7 +117,7 @@
          * Concrete MIME type of a document. For example, "image/png" or
          * "application/pdf" for openable files. A document can also be a
          * directory containing additional documents, which is represented with
-         * the {@link #MIME_TYPE_DIR} MIME type.
+         * the {@link #MIME_TYPE_DIR} MIME type. This column is required.
          * <p>
          * Type: STRING
          *
@@ -127,15 +127,15 @@
 
         /**
          * Display name of a document, used as the primary title displayed to a
-         * user.
+         * user. This column is required.
          * <p>
          * Type: STRING
          */
         public static final String COLUMN_DISPLAY_NAME = OpenableColumns.DISPLAY_NAME;
 
         /**
-         * Summary of a document, which may be shown to a user. The summary may
-         * be {@code null}.
+         * Summary of a document, which may be shown to a user. This column is
+         * optional, and may be {@code null}.
          * <p>
          * Type: STRING
          */
@@ -143,9 +143,9 @@
 
         /**
          * Timestamp when a document was last modified, in milliseconds since
-         * January 1, 1970 00:00:00.0 UTC, or {@code null} if unknown. A
-         * {@link DocumentsProvider} can update this field using events from
-         * {@link OnCloseListener} or other reliable
+         * January 1, 1970 00:00:00.0 UTC. This column is required, and may be
+         * {@code null} if unknown. A {@link DocumentsProvider} can update this
+         * field using events from {@link OnCloseListener} or other reliable
          * {@link ParcelFileDescriptor} transports.
          * <p>
          * Type: INTEGER (long)
@@ -155,15 +155,16 @@
         public static final String COLUMN_LAST_MODIFIED = "last_modified";
 
         /**
-         * Specific icon resource ID for a document, or {@code null} to use
-         * platform default icon based on {@link #COLUMN_MIME_TYPE}.
+         * Specific icon resource ID for a document. This column is optional,
+         * and may be {@code null} to use a platform-provided default icon based
+         * on {@link #COLUMN_MIME_TYPE}.
          * <p>
          * Type: INTEGER (int)
          */
         public static final String COLUMN_ICON = "icon";
 
         /**
-         * Flags that apply to a document.
+         * Flags that apply to a document. This column is required.
          * <p>
          * Type: INTEGER (int)
          *
@@ -171,12 +172,13 @@
          * @see #FLAG_SUPPORTS_DELETE
          * @see #FLAG_SUPPORTS_THUMBNAIL
          * @see #FLAG_DIR_PREFERS_GRID
-         * @see #FLAG_DIR_SUPPORTS_CREATE
+         * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
          */
         public static final String COLUMN_FLAGS = "flags";
 
         /**
-         * Size of a document, in bytes, or {@code null} if unknown.
+         * Size of a document, in bytes, or {@code null} if unknown. This column
+         * is required.
          * <p>
          * Type: INTEGER (long)
          */
@@ -211,7 +213,7 @@
          * writability of a document may change over time, for example due to
          * remote access changes. This flag indicates that a document client can
          * expect {@link ContentResolver#openOutputStream(Uri)} to succeed.
-         *
+         * 
          * @see #COLUMN_FLAGS
          */
         public static final int FLAG_SUPPORTS_WRITE = 1 << 1;
@@ -265,8 +267,9 @@
          *
          * @see #COLUMN_FLAGS
          * @see #FLAG_DIR_PREFERS_GRID
+         * @hide
          */
-        public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 6;
+        public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 16;
     }
 
     /**
@@ -282,31 +285,17 @@
         /**
          * Unique ID of a root. This ID is both provided by and interpreted by a
          * {@link DocumentsProvider}, and should be treated as an opaque value
-         * by client applications.
+         * by client applications. This column is required.
          * <p>
          * Type: STRING
          */
         public static final String COLUMN_ROOT_ID = "root_id";
 
         /**
-         * Type of a root, used for clustering when presenting multiple roots to
-         * a user.
+         * Flags that apply to a root. This column is required.
          * <p>
          * Type: INTEGER (int)
          *
-         * @see #ROOT_TYPE_SERVICE
-         * @see #ROOT_TYPE_SHORTCUT
-         * @see #ROOT_TYPE_DEVICE
-         */
-        public static final String COLUMN_ROOT_TYPE = "root_type";
-
-        /**
-         * Flags that apply to a root.
-         * <p>
-         * Type: INTEGER (int)
-         *
-         * @see #FLAG_ADVANCED
-         * @see #FLAG_EMPTY
          * @see #FLAG_LOCAL_ONLY
          * @see #FLAG_SUPPORTS_CREATE
          * @see #FLAG_SUPPORTS_RECENTS
@@ -315,22 +304,23 @@
         public static final String COLUMN_FLAGS = "flags";
 
         /**
-         * Icon resource ID for a root.
+         * Icon resource ID for a root. This column is required.
          * <p>
          * Type: INTEGER (int)
          */
         public static final String COLUMN_ICON = "icon";
 
         /**
-         * Title for a root, which will be shown to a user.
+         * Title for a root, which will be shown to a user. This column is
+         * required.
          * <p>
          * Type: STRING
          */
         public static final String COLUMN_TITLE = "title";
 
         /**
-         * Summary for this root, which may be shown to a user. The summary may
-         * be {@code null}.
+         * Summary for this root, which may be shown to a user. This column is
+         * optional, and may be {@code null}.
          * <p>
          * Type: STRING
          */
@@ -338,7 +328,7 @@
 
         /**
          * Document which is a directory that represents the top directory of
-         * this root.
+         * this root. This column is required.
          * <p>
          * Type: STRING
          *
@@ -347,20 +337,20 @@
         public static final String COLUMN_DOCUMENT_ID = "document_id";
 
         /**
-         * Number of bytes available in this root, or {@code null} if unknown or
-         * unbounded.
+         * Number of bytes available in this root. This column is optional, and
+         * may be {@code null} if unknown or unbounded.
          * <p>
          * Type: INTEGER (long)
          */
         public static final String COLUMN_AVAILABLE_BYTES = "available_bytes";
 
         /**
-         * MIME types supported by this root, or {@code null} if the root
-         * supports all MIME types. Multiple MIME types can be separated by a
-         * newline. For example, a root supporting audio might use
-         * "audio/*\napplication/x-flac".
+         * MIME types supported by this root. This column is optional, and if
+         * {@code null} the root is assumed to support all MIME types. Multiple
+         * MIME types can be separated by a newline. For example, a root
+         * supporting audio might return "audio/*\napplication/x-flac".
          * <p>
-         * Type: String
+         * Type: STRING
          */
         public static final String COLUMN_MIME_TYPES = "mime_types";
 
@@ -368,29 +358,6 @@
         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.
-         *
-         * @see #COLUMN_ROOT_TYPE
-         */
-        public static final int ROOT_TYPE_SERVICE = 1;
-
-        /**
-         * Type of root that represents a shortcut to content that may be
-         * available elsewhere through another storage root.
-         *
-         * @see #COLUMN_ROOT_TYPE
-         */
-        public static final int ROOT_TYPE_SHORTCUT = 2;
-
-        /**
-         * Type of root that represents a physical storage device.
-         *
-         * @see #COLUMN_ROOT_TYPE
-         */
-        public static final int ROOT_TYPE_DEVICE = 3;
-
-        /**
          * Flag indicating that at least one directory under this root supports
          * creating content. Roots with this flag will be shown when an
          * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}.
@@ -409,21 +376,13 @@
         public static final int FLAG_LOCAL_ONLY = 1 << 1;
 
         /**
-         * Flag indicating that this root should only be visible to advanced
-         * users.
-         *
-         * @see #COLUMN_FLAGS
-         */
-        public static final int FLAG_ADVANCED = 1 << 2;
-
-        /**
          * Flag indicating that this root can report recently modified
          * documents.
          *
          * @see #COLUMN_FLAGS
          * @see DocumentsContract#buildRecentDocumentsUri(String, String)
          */
-        public static final int FLAG_SUPPORTS_RECENTS = 1 << 3;
+        public static final int FLAG_SUPPORTS_RECENTS = 1 << 2;
 
         /**
          * Flag indicating that this root supports search.
@@ -432,19 +391,31 @@
          * @see DocumentsProvider#querySearchDocuments(String, String,
          *      String[])
          */
-        public static final int FLAG_SUPPORTS_SEARCH = 1 << 4;
+        public static final int FLAG_SUPPORTS_SEARCH = 1 << 3;
 
         /**
          * Flag indicating that this root is currently empty. This may be used
          * to hide the root when opening documents, but the root will still be
          * shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is
-         * also set.
+         * also set. If the value of this flag changes, such as when a root
+         * becomes non-empty, you must send a content changed notification for
+         * {@link DocumentsContract#buildRootsUri(String)}.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsProvider#querySearchDocuments(String, String,
-         *      String[])
+         * @see ContentResolver#notifyChange(Uri,
+         *      android.database.ContentObserver, boolean)
+         * @hide
          */
-        public static final int FLAG_EMPTY = 1 << 5;
+        public static final int FLAG_EMPTY = 1 << 16;
+
+        /**
+         * Flag indicating that this root should only be visible to advanced
+         * users.
+         *
+         * @see #COLUMN_FLAGS
+         * @hide
+         */
+        public static final int FLAG_ADVANCED = 1 << 17;
     }
 
     /**
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
index 0240874..904d525 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..403eddb
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
index 0240874..068619b 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..9a9cf5e
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
index 0240874..e38a868 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..205c34b
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
index 0240874..0b332e4 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..32b5f98
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..f47d50a
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index 3bea166..851061f 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -21,17 +21,18 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <FrameLayout
         android:id="@android:id/icon"
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
         android:layout_marginStart="12dp"
-        android:layout_marginEnd="20dp"
-        android:layout_gravity="center_vertical">
+        android:layout_marginEnd="20dp">
 
         <ImageView
             android:id="@+id/icon_mime"
@@ -49,11 +50,11 @@
 
     </FrameLayout>
 
+    <!-- This is the one special case where we want baseline alignment! -->
     <LinearLayout
-        android:layout_width="0dip"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:layout_gravity="center_vertical"
         android:orientation="horizontal">
 
         <TextView
diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
index 78735fd..9286277 100644
--- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
@@ -17,7 +17,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <FrameLayout
         android:layout_width="wrap_content"
@@ -47,7 +48,7 @@
         <com.android.documentsui.DirectoryContainerView
             android:id="@+id/container_directory"
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="0dp"
             android:layout_weight="1" />
 
         <FrameLayout
diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml
index 9937c39..2ef7e9c 100644
--- a/packages/DocumentsUI/res/layout/activity.xml
+++ b/packages/DocumentsUI/res/layout/activity.xml
@@ -27,7 +27,7 @@
         <com.android.documentsui.DirectoryContainerView
             android:id="@+id/container_directory"
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="0dp"
             android:layout_weight="1" />
 
         <FrameLayout
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index 09782d9..c3a3da0 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -18,4 +18,4 @@
     android:id="@android:id/list"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:divider="@null" />
+    android:divider="@drawable/ic_drawer_hairline_divider" />
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 570b517..891f0a0 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -29,6 +29,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
+        android:baselineAligned="false"
         android:gravity="center_vertical"
         android:background="#ddd"
         android:minHeight="?android:attr/listPreferredItemHeightSmall">
@@ -44,7 +45,7 @@
 
         <EditText
             android:id="@android:id/title"
-            android:layout_width="0dip"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:singleLine="true"
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index b745bb9..bb5dce1 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -28,37 +28,25 @@
 
         <FrameLayout
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="0dp"
             android:layout_weight="1"
             android:layout_marginBottom="6dp"
-            android:background="#fff">
-
-            <FrameLayout
-                android:id="@android:id/icon"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent">
-
-                <ImageView
-                    android:id="@+id/icon_mime"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:scaleType="centerInside"
-                    android:contentDescription="@null" />
-
-                <ImageView
-                    android:id="@+id/icon_thumb"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:scaleType="centerCrop"
-                    android:contentDescription="@null" />
-
-            </FrameLayout>
+            android:background="#fff"
+            android:foreground="@drawable/ic_grid_gradient_bg"
+            android:foregroundGravity="fill">
 
             <ImageView
+                android:id="@+id/icon_mime"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:scaleType="fitXY"
-                android:src="@drawable/ic_grid_gradient_bg"
+                android:scaleType="centerInside"
+                android:contentDescription="@null" />
+
+            <ImageView
+                android:id="@+id/icon_thumb"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scaleType="centerCrop"
                 android:contentDescription="@null" />
 
         </FrameLayout>
@@ -67,7 +55,9 @@
             android:id="@+id/line1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:gravity="center_vertical"
             android:orientation="horizontal"
+            android:baselineAligned="false"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
@@ -85,7 +75,7 @@
                 android:id="@android:id/icon1"
                 android:layout_width="@dimen/root_icon_size"
                 android:layout_height="@dimen/root_icon_size"
-                android:layout_marginStart="8dip"
+                android:layout_marginStart="8dp"
                 android:scaleType="centerInside"
                 android:contentDescription="@null" />
 
@@ -95,16 +85,17 @@
             android:id="@+id/line2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:gravity="center_vertical"
             android:orientation="horizontal"
+            android:baselineAligned="false"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
             <TextView
                 android:id="@+id/date"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:minWidth="80dp"
+                android:layout_weight="0.5"
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:textAlignment="viewStart"
@@ -112,26 +103,20 @@
 
             <TextView
                 android:id="@+id/size"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
+                android:layout_weight="0.5"
                 android:layout_marginStart="8dp"
-                android:minWidth="80dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:textAlignment="viewStart"
                 style="@style/TextAppearance.Small" />
 
-            <Space
-                android:layout_width="0dp"
-                android:layout_height="0dp"
-                android:layout_weight="1" />
-
             <ImageView
                 android:id="@android:id/icon2"
                 android:layout_width="@dimen/root_icon_size"
                 android:layout_height="@dimen/root_icon_size"
-                android:layout_marginStart="8dip"
+                android:layout_marginStart="8dp"
                 android:scaleType="centerInside"
                 android:contentDescription="@null"
                 android:visibility="gone" />
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 84fda9d..4c5b2e3 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -21,17 +21,18 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <FrameLayout
         android:id="@android:id/icon"
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
         android:layout_marginStart="12dp"
-        android:layout_marginEnd="20dp"
-        android:layout_gravity="center_vertical">
+        android:layout_marginEnd="20dp">
 
         <ImageView
             android:id="@+id/icon_mime"
@@ -50,20 +51,20 @@
     </FrameLayout>
 
     <LinearLayout
-        android:layout_width="0dip"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:layout_gravity="center_vertical"
         android:orientation="vertical">
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:orientation="horizontal"
+            android:baselineAligned="false">
 
             <TextView
                 android:id="@android:id/title"
-                android:layout_width="0dip"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
                 android:singleLine="true"
@@ -75,7 +76,7 @@
                 android:id="@android:id/icon1"
                 android:layout_width="@dimen/root_icon_size"
                 android:layout_height="@dimen/root_icon_size"
-                android:layout_marginStart="8dip"
+                android:layout_marginStart="8dp"
                 android:scaleType="centerInside"
                 android:contentDescription="@null" />
 
@@ -85,13 +86,15 @@
             android:id="@+id/line2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:baselineAligned="false">
 
             <TextView
                 android:id="@+id/date"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
+                android:layout_weight="0.25"
                 android:minWidth="70dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
@@ -100,11 +103,11 @@
 
             <TextView
                 android:id="@+id/size"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:minWidth="70dp"
+                android:layout_weight="0.25"
                 android:layout_marginStart="8dp"
+                android:minWidth="70dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:textAlignment="viewStart"
@@ -114,8 +117,7 @@
                 android:id="@android:id/summary"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:layout_gravity="center_vertical"
+                android:layout_weight="0.5"
                 android:layout_marginStart="8dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml
index 21be137..0bf6137 100644
--- a/packages/DocumentsUI/res/layout/item_loading_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_loading_grid.xml
@@ -20,8 +20,8 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
     android:orientation="horizontal">
 
     <ProgressBar
diff --git a/packages/DocumentsUI/res/layout/item_loading_list.xml b/packages/DocumentsUI/res/layout/item_loading_list.xml
index 7da71e3..cdcd01d 100644
--- a/packages/DocumentsUI/res/layout/item_loading_list.xml
+++ b/packages/DocumentsUI/res/layout/item_loading_list.xml
@@ -20,9 +20,8 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp">
 
     <ProgressBar
         android:layout_width="wrap_content"
diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml
index ffda98c..2bcbc2d 100644
--- a/packages/DocumentsUI/res/layout/item_message_list.xml
+++ b/packages/DocumentsUI/res/layout/item_message_list.xml
@@ -21,15 +21,16 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <ImageView
         android:id="@android:id/icon"
         android:layout_width="@android:dimen/app_icon_size"
         android:layout_height="@android:dimen/app_icon_size"
-        android:layout_marginEnd="8dip"
+        android:layout_marginEnd="8dp"
         android:layout_gravity="center_vertical"
         android:scaleType="centerInside"
         android:contentDescription="@null" />
diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml
index 98d78da..9b52d85 100644
--- a/packages/DocumentsUI/res/layout/item_root.xml
+++ b/packages/DocumentsUI/res/layout/item_root.xml
@@ -22,13 +22,14 @@
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:gravity="center_vertical"
     android:orientation="horizontal"
+    android:baselineAligned="false"
     android:background="@drawable/item_root">
 
     <ImageView
         android:id="@android:id/icon"
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
-        android:layout_marginEnd="8dip"
+        android:layout_marginEnd="8dp"
         android:scaleType="centerInside"
         android:contentDescription="@null" />
 
diff --git a/packages/DocumentsUI/res/layout/item_root_spacer.xml b/packages/DocumentsUI/res/layout/item_root_spacer.xml
new file mode 100644
index 0000000..7d96ac8
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_root_spacer.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/ic_drawer_tall_divider" />
diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_title.xml
index 7eb100a..58016f1 100644
--- a/packages/DocumentsUI/res/layout/item_title.xml
+++ b/packages/DocumentsUI/res/layout/item_title.xml
@@ -21,7 +21,8 @@
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:gravity="center_vertical"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <ImageView
         android:id="@+id/subdir"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 9d92cd8..48bfaf0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -25,6 +25,7 @@
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
@@ -35,6 +36,8 @@
 
 import com.android.documentsui.model.DocumentInfo;
 
+import java.io.FileNotFoundException;
+
 /**
  * Dialog to create a new directory.
  */
@@ -64,24 +67,45 @@
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 final String displayName = text1.getText().toString();
-
-                final DocumentsActivity activity = (DocumentsActivity) getActivity();
-                final DocumentInfo cwd = activity.getCurrentDirectory();
-
-                try {
-                    final Uri childUri = DocumentsContract.createDocument(
-                            resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, displayName);
-
-                    // Navigate into newly created child
-                    final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri);
-                    activity.onDocumentPicked(childDoc);
-                } catch (Exception e) {
-                    Toast.makeText(context, R.string.create_error, Toast.LENGTH_SHORT).show();
-                }
+                new CreateDirectoryTask(displayName).execute();
             }
         });
         builder.setNegativeButton(android.R.string.cancel, null);
 
         return builder.create();
     }
+
+    private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private final String mDisplayName;
+
+        public CreateDirectoryTask(String displayName) {
+            mDisplayName = displayName;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            final DocumentsActivity activity = (DocumentsActivity) getActivity();
+            final ContentResolver resolver = activity.getContentResolver();
+
+            final DocumentInfo cwd = activity.getCurrentDirectory();
+            final Uri childUri = DocumentsContract.createDocument(
+                    resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
+            try {
+                return DocumentInfo.fromUri(resolver, childUri);
+            } catch (FileNotFoundException e) {
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            final DocumentsActivity activity = (DocumentsActivity) getActivity();
+            if (result != null) {
+                // Navigate into newly created child
+                activity.onDocumentPicked(result);
+            } else {
+                Toast.makeText(activity, R.string.create_error, Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index c46dfb2..1f11aed 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -742,7 +742,6 @@
             final View line1 = convertView.findViewById(R.id.line1);
             final View line2 = convertView.findViewById(R.id.line2);
 
-            final View icon = convertView.findViewById(android.R.id.icon);
             final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
             final ImageView iconThumb = (ImageView) convertView.findViewById(R.id.icon_thumb);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
@@ -786,10 +785,12 @@
             // loaded in background.
             if (cacheHit) {
                 iconMime.setAlpha(0f);
+                iconMime.setImageDrawable(null);
                 iconThumb.setAlpha(1f);
             } else {
                 iconMime.setAlpha(1f);
                 iconThumb.setAlpha(0f);
+                iconThumb.setImageDrawable(null);
                 if (docIcon != 0) {
                     iconMime.setImageDrawable(
                             IconUtils.loadPackageIcon(context, docAuthority, docIcon));
@@ -895,12 +896,14 @@
             final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
             if (enabled) {
                 setEnabledRecursive(convertView, true);
-                icon.setAlpha(1f);
+                iconMime.setAlpha(1f);
+                iconThumb.setAlpha(1f);
                 if (icon1 != null) icon1.setAlpha(1f);
                 if (icon2 != null) icon2.setAlpha(1f);
             } else {
                 setEnabledRecursive(convertView, false);
-                icon.setAlpha(0.5f);
+                iconMime.setAlpha(0.5f);
+                iconThumb.setAlpha(0.5f);
                 if (icon1 != null) icon1.setAlpha(0.5f);
                 if (icon2 != null) icon2.setAlpha(0.5f);
             }
@@ -991,10 +994,11 @@
                 mIconThumb.setTag(null);
                 mIconThumb.setImageBitmap(result);
 
-                mIconMime.setAlpha(1f);
+                final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f;
+                mIconMime.setAlpha(targetAlpha);
                 mIconMime.animate().alpha(0f).start();
                 mIconThumb.setAlpha(0f);
-                mIconThumb.animate().alpha(1f).start();
+                mIconThumb.animate().alpha(targetAlpha).start();
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 8627ecf..0b3ecf8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -63,6 +63,9 @@
 }
 
 public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
+
+    private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
+
     private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
 
     private final int mType;
@@ -164,8 +167,7 @@
 
             if (mType == DirectoryFragment.TYPE_SEARCH) {
                 // Filter directories out of search results, for now
-                cursor = new FilteringCursorWrapper(cursor, null, new String[] {
-                        Document.MIME_TYPE_DIR });
+                cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
             } else {
                 // Normal directories should have sorting applied
                 cursor = new SortingCursorWrapper(cursor, result.sortOrder);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 72fdc57..4caec8f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -854,14 +854,7 @@
         mState.stackTouched = true;
 
         if (!mRoots.isRecentsRoot(root)) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
-                final DocumentInfo doc = DocumentInfo.fromUri(getContentResolver(), uri);
-                mState.stack.push(doc);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
-            } catch (FileNotFoundException e) {
-            }
+            new PickRootTask(root).execute();
         } else {
             onCurrentDirectoryChanged(ANIM_SIDE);
         }
@@ -871,6 +864,34 @@
         }
     }
 
+    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private RootInfo mRoot;
+
+        public PickRootTask(RootInfo root) {
+            mRoot = root;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            try {
+                final Uri uri = DocumentsContract.buildDocumentUri(
+                        mRoot.authority, mRoot.documentId);
+                return DocumentInfo.fromUri(getContentResolver(), uri);
+            } catch (FileNotFoundException e) {
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            if (result != null) {
+                mState.stack.push(result);
+                mState.stackTouched = true;
+                onCurrentDirectoryChanged(ANIM_SIDE);
+            }
+        }
+    }
+
     public void onAppPicked(ResolveInfo info) {
         final Intent intent = new Intent(getIntent());
         intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -909,7 +930,7 @@
             onCurrentDirectoryChanged(ANIM_DOWN);
         } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
             // Explicit file picked, return
-            onFinished(doc.derivedUri);
+            new ExistingFinishTask(doc.derivedUri).execute();
         } else if (mState.action == ACTION_CREATE) {
             // Replace selected file
             SaveFragment.get(fm).setReplaceTarget(doc);
@@ -943,29 +964,19 @@
             for (int i = 0; i < size; i++) {
                 uris[i] = docs.get(i).derivedUri;
             }
-            onFinished(uris);
+            new ExistingFinishTask(uris).execute();
         }
     }
 
     public void onSaveRequested(DocumentInfo replaceTarget) {
-        onFinished(replaceTarget.derivedUri);
+        new ExistingFinishTask(replaceTarget.derivedUri).execute();
     }
 
     public void onSaveRequested(String mimeType, String displayName) {
-        final DocumentInfo cwd = getCurrentDirectory();
-
-        final Uri childUri = DocumentsContract.createDocument(
-                getContentResolver(), cwd.derivedUri, mimeType, displayName);
-        if (childUri != null) {
-            onFinished(childUri);
-        } else {
-            Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
-        }
+        new CreateFinishTask(mimeType, displayName).execute();
     }
 
-    private void onFinished(Uri... uris) {
-        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
-
+    private void saveStackBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
@@ -973,6 +984,7 @@
         if (mState.action == ACTION_CREATE) {
             // Remember stack for last create
             values.clear();
+            values.put(RecentColumns.KEY, mState.stack.buildKey());
             values.put(RecentColumns.STACK, rawStack);
             resolver.insert(RecentsProvider.buildRecent(), values);
         }
@@ -983,6 +995,10 @@
         values.put(ResumeColumns.STACK, rawStack);
         values.put(ResumeColumns.EXTERNAL, 0);
         resolver.insert(RecentsProvider.buildResume(packageName), values);
+    }
+
+    private void onFinished(Uri... uris) {
+        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
         if (uris.length == 1) {
@@ -1008,6 +1024,56 @@
         finish();
     }
 
+    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+        private final String mMimeType;
+        private final String mDisplayName;
+
+        public CreateFinishTask(String mimeType, String displayName) {
+            mMimeType = mimeType;
+            mDisplayName = displayName;
+        }
+
+        @Override
+        protected Uri doInBackground(Void... params) {
+            final DocumentInfo cwd = getCurrentDirectory();
+            final Uri childUri = DocumentsContract.createDocument(
+                    getContentResolver(), cwd.derivedUri, mMimeType, mDisplayName);
+            if (childUri != null) {
+                saveStackBlocking();
+            }
+            return childUri;
+        }
+
+        @Override
+        protected void onPostExecute(Uri result) {
+            if (result != null) {
+                onFinished(result);
+            } else {
+                Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
+                        .show();
+            }
+        }
+    }
+
+    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri[] mUris;
+
+        public ExistingFinishTask(Uri... uris) {
+            mUris = uris;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onFinished(mUris);
+        }
+    }
+
     public static class State implements android.os.Parcelable {
         public int action;
         public String[] acceptMimes;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 5f56963..52d816f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -34,10 +34,15 @@
     private int mCount;
 
     public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) {
-        this(cursor, acceptMimes, null);
+        this(cursor, acceptMimes, null, Long.MIN_VALUE);
     }
 
     public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) {
+        this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE);
+    }
+
+    public FilteringCursorWrapper(
+            Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) {
         mCursor = cursor;
 
         final int count = cursor.getCount();
@@ -47,9 +52,14 @@
         while (cursor.moveToNext()) {
             final String mimeType = cursor.getString(
                     cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
+            final long lastModified = cursor.getLong(
+                    cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
             if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) {
                 continue;
             }
+            if (lastModified < rejectBefore) {
+                continue;
+            }
             if (MimePredicate.mimeMatches(acceptMimes, mimeType)) {
                 mPosition[mCount++] = cursor.getPosition();
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index e390456..9a4fb7d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -26,9 +26,11 @@
 import android.database.Cursor;
 import android.database.MergeCursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
+import android.text.format.DateUtils;
 import android.util.Log;
 
 import com.android.documentsui.DocumentsActivity.State;
@@ -54,17 +56,23 @@
 public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
     private static final boolean LOGD = true;
 
-    public static final int MAX_OUTSTANDING_RECENTS = 2;
+    // TODO: adjust for svelte devices
+    // TODO: add support for oneway queries to avoid wedging loader
+    private static final int MAX_OUTSTANDING_RECENTS = 2;
 
     /**
      * Time to wait for first pass to complete before returning partial results.
      */
-    public static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
+    private static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
 
-    /**
-     * Maximum documents from a single root.
-     */
-    public static final int MAX_DOCS_FROM_ROOT = 64;
+    /** Maximum documents from a single root. */
+    private static final int MAX_DOCS_FROM_ROOT = 64;
+
+    /** Ignore documents older than this age. */
+    private static final long REJECT_OLDER_THAN = 45 * DateUtils.DAY_IN_MILLIS;
+
+    /** MIME types that should always be excluded from recents. */
+    private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
 
     private static final ExecutorService sExecutor = buildExecutor();
 
@@ -173,6 +181,8 @@
             }
         }
 
+        final long rejectBefore = System.currentTimeMillis() - REJECT_OLDER_THAN;
+
         // Collect all finished tasks
         List<Cursor> cursors = Lists.newArrayList();
         for (RecentTask task : mTasks.values()) {
@@ -180,7 +190,7 @@
                 try {
                     final Cursor cursor = task.get();
                     final FilteringCursorWrapper filtered = new FilteringCursorWrapper(
-                            cursor, mState.acceptMimes, new String[] { Document.MIME_TYPE_DIR }) {
+                            cursor, mState.acceptMimes, RECENT_REJECT_MIMES, rejectBefore) {
                         @Override
                         public void close() {
                             // Ignored, since we manage cursor lifecycle internally
@@ -203,11 +213,22 @@
         final DirectoryResult result = new DirectoryResult();
         result.sortOrder = SORT_ORDER_LAST_MODIFIED;
 
-        if (cursors.size() > 0) {
-            final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
-            final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder);
-            result.cursor = sorted;
+        // Hint to UI if we're still loading
+        final Bundle extras = new Bundle();
+        if (cursors.size() != mTasks.size()) {
+            extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
         }
+
+        final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
+        final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) {
+            @Override
+            public Bundle getExtras() {
+                return extras;
+            }
+        };
+
+        result.cursor = sorted;
+
         return result;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index c975382..3954173 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -66,6 +66,7 @@
  */
 public class RecentsCreateFragment extends Fragment {
 
+    private View mEmptyView;
     private ListView mListView;
 
     private DocumentStackAdapter mAdapter;
@@ -87,6 +88,8 @@
 
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
+        mEmptyView = view.findViewById(android.R.id.empty);
+
         mListView = (ListView) view.findViewById(R.id.list);
         mListView.setOnItemClickListener(mItemListener);
 
@@ -189,6 +192,13 @@
 
         public void swapStacks(List<DocumentStack> stacks) {
             mStacks = stacks;
+
+            if (isEmpty()) {
+                mEmptyView.setVisibility(View.VISIBLE);
+            } else {
+                mEmptyView.setVisibility(View.GONE);
+            }
+
             notifyDataSetChanged();
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 7386cae..4313fa7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -33,7 +33,7 @@
 public class RecentsProvider extends ContentProvider {
     private static final String TAG = "RecentsProvider";
 
-    public static final long MAX_HISTORY_IN_MILLIS = DateUtils.DAY_IN_MILLIS * 45;
+    public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
 
     private static final String AUTHORITY = "com.android.documentsui.recents";
 
@@ -56,6 +56,7 @@
     public static final String TABLE_RESUME = "resume";
 
     public static class RecentColumns {
+        public static final String KEY = "key";
         public static final String STACK = "stack";
         public static final String TIMESTAMP = "timestamp";
     }
@@ -99,16 +100,18 @@
         private static final int VERSION_INIT = 1;
         private static final int VERSION_AS_BLOB = 3;
         private static final int VERSION_ADD_EXTERNAL = 4;
+        private static final int VERSION_ADD_RECENT_KEY = 5;
 
         public DatabaseHelper(Context context) {
-            super(context, DB_NAME, null, VERSION_ADD_EXTERNAL);
+            super(context, DB_NAME, null, VERSION_ADD_RECENT_KEY);
         }
 
         @Override
         public void onCreate(SQLiteDatabase db) {
 
             db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" +
-                    RecentColumns.STACK + " BLOB PRIMARY KEY ON CONFLICT REPLACE," +
+                    RecentColumns.KEY + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
+                    RecentColumns.STACK + " BLOB DEFAULT NULL," +
                     RecentColumns.TIMESTAMP + " INTEGER" +
                     ")");
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 15af8aa..e3908e9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -99,7 +99,8 @@
      */
     public void updateAsync() {
         // Special root for recents
-        mRecentsRoot.rootType = Root.ROOT_TYPE_SHORTCUT;
+        mRecentsRoot.authority = null;
+        mRecentsRoot.rootId = null;
         mRecentsRoot.icon = R.drawable.ic_root_recent;
         mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE;
         mRecentsRoot.title = mContext.getString(R.string.root_recent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index d602622..2fb12bb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -26,7 +26,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
-import android.provider.DocumentsContract.Root;
 import android.text.TextUtils;
 import android.text.format.Formatter;
 import android.view.LayoutInflater;
@@ -37,16 +36,16 @@
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.ListView;
-import android.widget.Space;
 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;
 import com.android.internal.util.Objects;
+import com.google.common.collect.Lists;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
@@ -56,7 +55,7 @@
 public class RootsFragment extends Fragment {
 
     private ListView mList;
-    private SectionedRootsAdapter mAdapter;
+    private RootsAdapter mAdapter;
 
     private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
 
@@ -112,7 +111,7 @@
 
                 final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
 
-                mAdapter = new SectionedRootsAdapter(context, result, includeApps);
+                mAdapter = new RootsAdapter(context, result, includeApps);
                 mList.setAdapter(mAdapter);
 
                 onCurrentRootChanged();
@@ -154,136 +153,148 @@
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
-            final Object item = mAdapter.getItem(position);
-            if (item instanceof RootInfo) {
-                activity.onRootPicked((RootInfo) item, true);
-            } else if (item instanceof ResolveInfo) {
-                activity.onAppPicked((ResolveInfo) item);
+            final Item item = mAdapter.getItem(position);
+            if (item instanceof RootItem) {
+                activity.onRootPicked(((RootItem) item).root, true);
+            } else if (item instanceof AppItem) {
+                activity.onAppPicked(((AppItem) item).info);
             } else {
                 throw new IllegalStateException("Unknown root: " + item);
             }
         }
     };
 
-    private static class RootsAdapter extends ArrayAdapter<RootInfo> implements SectionAdapter {
-        public RootsAdapter(Context context) {
-            super(context, 0);
+    private static abstract class Item {
+        private final int mLayoutId;
+
+        public Item(int layoutId) {
+            mLayoutId = layoutId;
+        }
+
+        public View getView(View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(mLayoutId, parent, false);
+            }
+            bindView(convertView);
+            return convertView;
+        }
+
+        public abstract void bindView(View convertView);
+    }
+
+    private static class RootItem extends Item {
+        public final RootInfo root;
+
+        public RootItem(RootInfo root) {
+            super(R.layout.item_root);
+            this.root = root;
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
-            if (convertView == null) {
-                convertView = LayoutInflater.from(context)
-                        .inflate(R.layout.item_root, parent, false);
-            }
-
+        public void bindView(View convertView) {
             final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
 
-            final RootInfo root = getItem(position);
+            final Context context = convertView.getContext();
             icon.setImageDrawable(root.loadIcon(context));
             title.setText(root.title);
 
-            // Device summary is always available space
-            final String summaryText;
-            if (root.rootType == Root.ROOT_TYPE_DEVICE && root.availableBytes >= 0) {
+            // Show available space if no summary
+            String summaryText = root.summary;
+            if (TextUtils.isEmpty(summaryText) && root.availableBytes >= 0) {
                 summaryText = context.getString(R.string.root_available_bytes,
                         Formatter.formatFileSize(context, root.availableBytes));
-            } else {
-                summaryText = root.summary;
             }
 
             summary.setText(summaryText);
             summary.setVisibility(TextUtils.isEmpty(summaryText) ? View.GONE : View.VISIBLE);
-
-            return convertView;
-        }
-
-        @Override
-        public View getHeaderView(View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = new Space(parent.getContext());
-            }
-            return convertView;
         }
     }
 
-    private static class AppsAdapter extends ArrayAdapter<ResolveInfo> implements SectionAdapter {
-        public AppsAdapter(Context context) {
-            super(context, 0);
+    private static class SpacerItem extends Item {
+        public SpacerItem() {
+            super(R.layout.item_root_spacer);
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
-            final PackageManager pm = context.getPackageManager();
-            if (convertView == null) {
-                convertView = LayoutInflater.from(context)
-                        .inflate(R.layout.item_root, parent, false);
-            }
+        public void bindView(View convertView) {
+            // Nothing to bind
+        }
+    }
 
+    private static class AppItem extends Item {
+        public final ResolveInfo info;
+
+        public AppItem(ResolveInfo info) {
+            super(R.layout.item_root);
+            this.info = info;
+        }
+
+        @Override
+        public void bindView(View convertView) {
             final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
 
-            final ResolveInfo info = getItem(position);
+            final PackageManager pm = convertView.getContext().getPackageManager();
             icon.setImageDrawable(info.loadIcon(pm));
             title.setText(info.loadLabel(pm));
 
             // TODO: match existing summary behavior from disambig dialog
             summary.setVisibility(View.GONE);
-
-            return convertView;
-        }
-
-        @Override
-        public View getHeaderView(View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_root_header, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            title.setText(R.string.root_type_apps);
-
-            return convertView;
         }
     }
 
-    private static class SectionedRootsAdapter extends SectionedListAdapter {
-        private final RootsAdapter mRecent;
-        private final RootsAdapter mServices;
-        private final RootsAdapter mShortcuts;
-        private final RootsAdapter mDevices;
-        private final AppsAdapter mApps;
+    private static class RootsAdapter extends ArrayAdapter<Item> {
+        public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) {
+            super(context, 0);
 
-        public SectionedRootsAdapter(
-                Context context, Collection<RootInfo> roots, Intent includeApps) {
-            mRecent = new RootsAdapter(context);
-            mServices = new RootsAdapter(context);
-            mShortcuts = new RootsAdapter(context);
-            mDevices = new RootsAdapter(context);
-            mApps = new AppsAdapter(context);
+            RootItem recents = null;
+            RootItem images = null;
+            RootItem videos = null;
+            RootItem audio = null;
+            RootItem downloads = null;
+
+            final List<RootInfo> clouds = Lists.newArrayList();
+            final List<RootInfo> locals = Lists.newArrayList();
 
             for (RootInfo root : roots) {
-                if (root.authority == null) {
-                    mRecent.add(root);
-                    continue;
+                if (root.isRecents()) {
+                    recents = new RootItem(root);
+                } else if (root.isExternalStorage()) {
+                    locals.add(root);
+                } else if (root.isDownloads()) {
+                    downloads = new RootItem(root);
+                } else if (root.isImages()) {
+                    images = new RootItem(root);
+                } else if (root.isVideos()) {
+                    videos = new RootItem(root);
+                } else if (root.isAudio()) {
+                    audio = new RootItem(root);
+                } else {
+                    clouds.add(root);
                 }
+            }
 
-                switch (root.rootType) {
-                    case Root.ROOT_TYPE_SERVICE:
-                        mServices.add(root);
-                        break;
-                    case Root.ROOT_TYPE_SHORTCUT:
-                        mShortcuts.add(root);
-                        break;
-                    case Root.ROOT_TYPE_DEVICE:
-                        mDevices.add(root);
-                        break;
-                }
+            final RootComparator comp = new RootComparator();
+            Collections.sort(clouds, comp);
+            Collections.sort(locals, comp);
+
+            if (recents != null) add(recents);
+
+            for (RootInfo cloud : clouds) {
+                add(new RootItem(cloud));
+            }
+
+            if (images != null) add(images);
+            if (videos != null) add(videos);
+            if (audio != null) add(audio);
+            if (downloads != null) add(downloads);
+
+            for (RootInfo local : locals) {
+                add(new RootItem(local));
             }
 
             if (includeApps != null) {
@@ -291,34 +302,53 @@
                 final List<ResolveInfo> infos = pm.queryIntentActivities(
                         includeApps, PackageManager.MATCH_DEFAULT_ONLY);
 
+                final List<AppItem> apps = Lists.newArrayList();
+
                 // Omit ourselves from the list
                 for (ResolveInfo info : infos) {
                     if (!context.getPackageName().equals(info.activityInfo.packageName)) {
-                        mApps.add(info);
+                        apps.add(new AppItem(info));
+                    }
+                }
+
+                if (apps.size() > 0) {
+                    add(new SpacerItem());
+                    for (Item item : apps) {
+                        add(item);
                     }
                 }
             }
+        }
 
-            final RootComparator comp = new RootComparator();
-            mServices.sort(comp);
-            mShortcuts.sort(comp);
-            mDevices.sort(comp);
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final Item item = getItem(position);
+            return item.getView(convertView, parent);
+        }
 
-            if (mRecent.getCount() > 0) {
-                addSection(mRecent);
+        @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItemViewType(position) != 1;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            final Item item = getItem(position);
+            if (item instanceof RootItem || item instanceof AppItem) {
+                return 0;
+            } else {
+                return 1;
             }
-            if (mServices.getCount() > 0) {
-                addSection(mServices);
-            }
-            if (mShortcuts.getCount() > 0) {
-                addSection(mShortcuts);
-            }
-            if (mDevices.getCount() > 0) {
-                addSection(mDevices);
-            }
-            if (mApps.getCount() > 0) {
-                addSection(mApps);
-            }
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 2;
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
index 57fc7e4..1a47308 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -213,8 +213,13 @@
                 if (DocumentsContract.isDocumentUri(this, uri)) {
                     result += "; DOC_ID";
                 }
-                getContentResolver()
-                        .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                try {
+                    getContentResolver().takePersistableUriPermission(
+                            uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                } catch (SecurityException e) {
+                    result += "; FAILED TO TAKE";
+                    Log.e(TAG, "Failed to take", e);
+                }
                 InputStream is = null;
                 try {
                     is = getContentResolver().openInputStream(uri);
@@ -222,7 +227,7 @@
                     result += "; read length=" + length;
                 } catch (Exception e) {
                     result += "; ERROR";
-                    Log.w(TAG, "Failed to read " + uri, e);
+                    Log.e(TAG, "Failed to read " + uri, e);
                 } finally {
                     IoUtils.closeQuietly(is);
                 }
@@ -235,15 +240,20 @@
                 if (DocumentsContract.isDocumentUri(this, uri)) {
                     result += "; DOC_ID";
                 }
-                getContentResolver()
-                        .takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                try {
+                    getContentResolver().takePersistableUriPermission(
+                            uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                } catch (SecurityException e) {
+                    result += "; FAILED TO TAKE";
+                    Log.e(TAG, "Failed to take", e);
+                }
                 OutputStream os = null;
                 try {
                     os = getContentResolver().openOutputStream(uri);
                     os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes());
                 } catch (Exception e) {
                     result += "; ERROR";
-                    Log.w(TAG, "Failed to write " + uri, e);
+                    Log.e(TAG, "Failed to write " + uri, e);
                 } finally {
                     IoUtils.closeQuietly(os);
                 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index 0a378c0..28bab6c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -71,6 +71,25 @@
         }
     }
 
+    /**
+     * Build key that uniquely identifies this stack. It omits most of the raw
+     * details included in {@link #write(DataOutputStream)}, since they change
+     * too regularly to be used as a key.
+     */
+    public String buildKey() {
+        final StringBuilder builder = new StringBuilder();
+        if (root != null) {
+            builder.append(root.authority).append('#');
+            builder.append(root.rootId).append('#');
+        } else {
+            builder.append("[null]").append('#');
+        }
+        for (DocumentInfo doc : this) {
+            builder.append(doc.documentId).append('#');
+        }
+        return builder.toString();
+    }
+
     @Override
     public void reset() {
         clear();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 014901a..e220c9e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -42,10 +42,10 @@
  */
 public class RootInfo implements Durable, Parcelable {
     private static final int VERSION_INIT = 1;
+    private static final int VERSION_DROP_TYPE = 2;
 
     public String authority;
     public String rootId;
-    public int rootType;
     public int flags;
     public int icon;
     public String title;
@@ -67,7 +67,6 @@
     public void reset() {
         authority = null;
         rootId = null;
-        rootType = 0;
         flags = 0;
         icon = 0;
         title = null;
@@ -85,10 +84,9 @@
     public void read(DataInputStream in) throws IOException {
         final int version = in.readInt();
         switch (version) {
-            case VERSION_INIT:
+            case VERSION_DROP_TYPE:
                 authority = DurableUtils.readNullableString(in);
                 rootId = DurableUtils.readNullableString(in);
-                rootType = in.readInt();
                 flags = in.readInt();
                 icon = in.readInt();
                 title = DurableUtils.readNullableString(in);
@@ -105,10 +103,9 @@
 
     @Override
     public void write(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION_INIT);
+        out.writeInt(VERSION_DROP_TYPE);
         DurableUtils.writeNullableString(out, authority);
         DurableUtils.writeNullableString(out, rootId);
-        out.writeInt(rootType);
         out.writeInt(flags);
         out.writeInt(icon);
         DurableUtils.writeNullableString(out, title);
@@ -146,7 +143,6 @@
         final RootInfo root = new RootInfo();
         root.authority = authority;
         root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
-        root.rootType = getCursorInt(cursor, Root.COLUMN_ROOT_TYPE);
         root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
         root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
         root.title = getCursorString(cursor, Root.COLUMN_TITLE);
@@ -162,25 +158,44 @@
         derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
 
         // TODO: remove these special case icons
-        if ("com.android.externalstorage.documents".equals(authority)) {
-            if ("documents".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_text;
-            } else {
-                derivedIcon = R.drawable.ic_root_sdcard;
-            }
-        }
-        if ("com.android.providers.downloads.documents".equals(authority)) {
+        if (isExternalStorage()) {
+            derivedIcon = R.drawable.ic_root_sdcard;
+        } else if (isDownloads()) {
             derivedIcon = R.drawable.ic_root_download;
+        } else if (isImages()) {
+            derivedIcon = R.drawable.ic_doc_image;
+        } else if (isVideos()) {
+            derivedIcon = R.drawable.ic_doc_video;
+        } else if (isAudio()) {
+            derivedIcon = R.drawable.ic_doc_audio;
         }
-        if ("com.android.providers.media.documents".equals(authority)) {
-            if ("images_root".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_image;
-            } else if ("videos_root".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_video;
-            } else if ("audio_root".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_audio;
-            }
-        }
+    }
+
+    public boolean isRecents() {
+        return authority == null && rootId == null;
+    }
+
+    public boolean isExternalStorage() {
+        return "com.android.externalstorage.documents".equals(authority);
+    }
+
+    public boolean isDownloads() {
+        return "com.android.providers.downloads.documents".equals(authority);
+    }
+
+    public boolean isImages() {
+        return "com.android.providers.media.documents".equals(authority)
+                && "images_root".equals(rootId);
+    }
+
+    public boolean isVideos() {
+        return "com.android.providers.media.documents".equals(authority)
+                && "videos_root".equals(rootId);
+    }
+
+    public boolean isAudio() {
+        return "com.android.providers.media.documents".equals(authority)
+                && "audio_root".equals(rootId);
     }
 
     @Override
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index ed28da5..9328b33 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -47,9 +47,8 @@
     // docId format: root:path/to/file
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
-            Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
-            Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
-            Root.COLUMN_AVAILABLE_BYTES,
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
+            Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
     };
 
     private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
@@ -59,7 +58,6 @@
 
     private static class RootInfo {
         public String rootId;
-        public int rootType;
         public int flags;
         public String title;
         public String docId;
@@ -84,7 +82,6 @@
 
             final RootInfo root = new RootInfo();
             root.rootId = rootId;
-            root.rootType = Root.ROOT_TYPE_DEVICE;
             root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
                     | Root.FLAG_SUPPORTS_SEARCH;
             root.title = getContext().getString(R.string.root_internal_storage);
@@ -198,7 +195,6 @@
 
             final RowBuilder row = result.newRow();
             row.add(Root.COLUMN_ROOT_ID, root.rootId);
-            row.add(Root.COLUMN_ROOT_TYPE, root.rootType);
             row.add(Root.COLUMN_FLAGS, root.flags);
             row.add(Root.COLUMN_TITLE, root.title);
             row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
index e6fbb1b..5a15cd2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -65,7 +65,7 @@
     private static final String MY_DOC_NULL = "myNull";
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
-            Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
             Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
             Root.COLUMN_AVAILABLE_BYTES,
     };
@@ -114,7 +114,6 @@
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
         final RowBuilder row = result.newRow();
         row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
-        row.add(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SERVICE);
         row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
         row.add(Root.COLUMN_TITLE, "_Test title which is really long");
         row.add(Root.COLUMN_SUMMARY,