Merge "Using Activity#setTitle to have TalkBack announce root traversal." into nyc-andromeda-dev
diff --git a/res/layout/document_debug_info.xml b/res/layout/document_debug_info.xml
new file mode 100644
index 0000000..83664af
--- /dev/null
+++ b/res/layout/document_debug_info.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<com.android.documentsui.ui.DocumentDebugInfo
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="@dimen/grid_item_margin"
+    android:textAlignment="viewStart"
+    android:typeface="monospace"
+    android:textSize="11sp"
+    android:textColor="#FF000000" />
diff --git a/res/layout/item_dir_grid.xml b/res/layout/item_dir_grid.xml
index 36af9b9..917f3f0 100644
--- a/res/layout/item_dir_grid.xml
+++ b/res/layout/item_dir_grid.xml
@@ -15,70 +15,83 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_margin="@dimen/grid_item_margin"
+    android:orientation="vertical"
     android:background="@color/item_doc_background"
     android:elevation="@dimen/grid_item_elevation"
-    android:focusable="true" >
+    android:focusable="true">
 
-    <!-- The height is 48px.
-         paddingTop (9dp) + @dimen/check_icon_size (30dp) + paddingBottom (9dp) -->
-    <LinearLayout
+    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="horizontal"
-        android:paddingBottom="9dp"
-        android:paddingLeft="9dp"
-        android:paddingRight="12dp"
-        android:paddingTop="9dp"
-        android:gravity="center_vertical">
+        android:layout_height="wrap_content">
 
-        <FrameLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp" >
+        <!-- The height is 48px.
+             paddingTop (9dp) + @dimen/check_icon_size (30dp) + paddingBottom (9dp) -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            android:paddingBottom="9dp"
+            android:paddingLeft="9dp"
+            android:paddingRight="12dp"
+            android:paddingTop="9dp"
+            android:gravity="center_vertical">
 
-            <ImageView
-                android:id="@+id/icon_mime_sm"
+            <FrameLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:contentDescription="@null"
-                android:scaleType="centerInside"
-                android:src="@drawable/ic_doc_folder" />
+                android:layout_marginEnd="8dp" >
 
-            <ImageView
-                android:id="@+id/icon_check"
-                android:layout_width="@dimen/check_icon_size"
-                android:layout_height="@dimen/check_icon_size"
-                android:alpha="0"
-                android:contentDescription="@null"
-                android:scaleType="fitCenter"
-                android:src="@drawable/ic_check_circle" />
+                <ImageView
+                    android:id="@+id/icon_mime_sm"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:contentDescription="@null"
+                    android:scaleType="centerInside"
+                    android:src="@drawable/ic_doc_folder" />
 
-        </FrameLayout>
+                <ImageView
+                    android:id="@+id/icon_check"
+                    android:layout_width="@dimen/check_icon_size"
+                    android:layout_height="@dimen/check_icon_size"
+                    android:alpha="0"
+                    android:contentDescription="@null"
+                    android:scaleType="fitCenter"
+                    android:src="@drawable/ic_check_circle" />
 
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:singleLine="true"
-            android:textAlignment="viewStart"
-            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-            android:textColor="@color/item_title" />
+            </FrameLayout>
 
-    </LinearLayout>
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textAlignment="viewStart"
+                android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                android:textColor="@color/item_title" />
 
-    <!-- An overlay that draws the item border when it is focused. -->
+        </LinearLayout>
 
-    <View
+        <!-- An overlay that draws the item border when it is focused. -->
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@drawable/item_doc_grid_border"
+            android:contentDescription="@null"
+            android:duplicateParentState="true" />
+
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/debug_info"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@drawable/item_doc_grid_border"
-        android:contentDescription="@null"
-        android:duplicateParentState="true" />
+        android:layout_height="wrap_content"
+        android:background="#FFEFEFEF" />
 
-</FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/item_doc_grid.xml b/res/layout/item_doc_grid.xml
index 0fa9685..5b8ba30 100644
--- a/res/layout/item_doc_grid.xml
+++ b/res/layout/item_doc_grid.xml
@@ -14,126 +14,138 @@
      limitations under the License.
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_margin="@dimen/grid_item_margin"
+    android:orientation="vertical"
     android:background="@color/item_doc_background"
     android:elevation="@dimen/grid_item_elevation"
     android:focusable="true">
 
-    <!-- Main item thumbnail.  Comprised of two overlapping images, the
-         visibility of which is controlled by code in
-         DirectoryFragment.java. -->
-
-    <FrameLayout
-        android:id="@+id/thumbnail"
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
 
-        <com.android.documentsui.GridItemThumbnail
-            android:id="@+id/icon_thumb"
+        <!-- Main item thumbnail.  Comprised of two overlapping images, the
+             visibility of which is controlled by code in
+             DirectoryFragment.java. -->
+
+        <FrameLayout
+            android:id="@+id/thumbnail"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <com.android.documentsui.GridItemThumbnail
+                android:id="@+id/icon_thumb"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:scaleType="centerCrop"
+                android:contentDescription="@null"
+                android:tint="@color/item_doc_grid_tint"
+                android:tintMode="src_over" />
+
+            <com.android.documentsui.GridItemThumbnail
+                android:id="@+id/icon_mime_lg"
+                android:layout_width="@dimen/icon_size"
+                android:layout_height="@dimen/icon_size"
+                android:layout_gravity="center"
+                android:scaleType="fitCenter"
+                android:contentDescription="@null" />
+
+        </FrameLayout>
+
+        <!-- Item nameplate.  Has a mime-type icon and some text fields (title,
+             size, mod-time, etc). -->
+
+        <RelativeLayout
+            android:id="@+id/nameplate"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:scaleType="centerCrop"
+            android:layout_below="@id/thumbnail"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:paddingLeft="12dp"
+            android:paddingRight="12dp">
+
+            <ImageView
+                android:id="@+id/icon_mime_sm"
+                android:layout_width="@dimen/grid_item_icon_size"
+                android:layout_height="@dimen/grid_item_icon_size"
+                android:layout_marginEnd="8dp"
+                android:layout_alignParentStart="true"
+                android:layout_centerVertical="true"
+                android:scaleType="center"
+                android:contentDescription="@null"/>
+
+            <ImageView
+                android:id="@+id/icon_check"
+                android:src="@drawable/ic_check_circle"
+                android:alpha="0"
+                android:layout_width="@dimen/check_icon_size"
+                android:layout_height="@dimen/check_icon_size"
+                android:layout_marginEnd="8dp"
+                android:layout_alignParentStart="true"
+                android:layout_centerVertical="true"
+                android:scaleType="fitCenter"
+                android:contentDescription="@null"/>
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:layout_toEndOf="@id/icon_mime_sm"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAlignment="viewStart"
+                android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                android:textColor="@color/item_title" />
+
+            <TextView
+                android:id="@+id/details"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/icon_mime_sm"
+                android:layout_below="@android:id/title"
+                android:layout_marginEnd="4dp"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAlignment="viewStart"
+                android:textAppearance="@android:style/TextAppearance.Material.Caption"
+                android:textColor="@color/item_details" />
+
+            <TextView
+                android:id="@+id/date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_toEndOf="@id/details"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAlignment="viewStart"
+                android:textAppearance="@android:style/TextAppearance.Material.Caption"
+                android:textColor="@color/item_details" />
+
+        </RelativeLayout>
+
+        <!-- An overlay that draws the item border when it is focused. -->
+        <View
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignBottom="@id/nameplate"
+            android:layout_alignTop="@id/thumbnail"
+            android:layout_alignLeft="@id/thumbnail"
+            android:layout_alignRight="@id/thumbnail"
             android:contentDescription="@null"
-            android:tint="@color/item_doc_grid_tint"
-            android:tintMode="src_over" />
-
-        <com.android.documentsui.GridItemThumbnail
-            android:id="@+id/icon_mime_lg"
-            android:layout_width="@dimen/icon_size"
-            android:layout_height="@dimen/icon_size"
-            android:layout_gravity="center"
-            android:scaleType="fitCenter"
-            android:contentDescription="@null" />
-
-    </FrameLayout>
-
-    <!-- Item nameplate.  Has a mime-type icon and some text fields (title,
-         size, mod-time, etc). -->
-
-    <RelativeLayout
-        android:id="@+id/nameplate"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/thumbnail"
-        android:paddingTop="8dp"
-        android:paddingBottom="8dp"
-        android:paddingLeft="12dp"
-        android:paddingRight="12dp">
-
-        <ImageView
-            android:id="@+id/icon_mime_sm"
-            android:layout_width="@dimen/grid_item_icon_size"
-            android:layout_height="@dimen/grid_item_icon_size"
-            android:layout_marginEnd="8dp"
-            android:layout_alignParentStart="true"
-            android:layout_centerVertical="true"
-            android:scaleType="center"
-            android:contentDescription="@null"/>
-
-        <ImageView
-            android:id="@+id/icon_check"
-            android:src="@drawable/ic_check_circle"
-            android:alpha="0"
-            android:layout_width="@dimen/check_icon_size"
-            android:layout_height="@dimen/check_icon_size"
-            android:layout_marginEnd="8dp"
-            android:layout_alignParentStart="true"
-            android:layout_centerVertical="true"
-            android:scaleType="fitCenter"
-            android:contentDescription="@null"/>
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            android:layout_toEndOf="@id/icon_mime_sm"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:textAlignment="viewStart"
-            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-            android:textColor="@color/item_title" />
-
-        <TextView
-            android:id="@+id/details"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_toEndOf="@id/icon_mime_sm"
-            android:layout_below="@android:id/title"
-            android:layout_marginEnd="4dp"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:textAlignment="viewStart"
-            android:textAppearance="@android:style/TextAppearance.Material.Caption"
-            android:textColor="@color/item_details" />
-
-        <TextView
-            android:id="@+id/date"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_toEndOf="@id/details"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:textAlignment="viewStart"
-            android:textAppearance="@android:style/TextAppearance.Material.Caption"
-            android:textColor="@color/item_details" />
+            android:background="@drawable/item_doc_grid_border"
+            android:duplicateParentState="true" />
 
     </RelativeLayout>
 
-    <!-- An overlay that draws the item border when it is focused. -->
-    <View
-        android:layout_width="wrap_content"
+    <FrameLayout
+        android:id="@+id/debug_info"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignBottom="@id/nameplate"
-        android:layout_alignTop="@id/thumbnail"
-        android:layout_alignLeft="@id/thumbnail"
-        android:layout_alignRight="@id/thumbnail"
-        android:contentDescription="@null"
-        android:background="@drawable/item_doc_grid_border"
-        android:duplicateParentState="true" />
-
-</RelativeLayout>
+        android:background="#FFEFEFEF" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ce6f247..ecd598e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -24,10 +24,9 @@
     <dimen name="list_item_thumbnail_size">40dp</dimen>
     <dimen name="grid_item_icon_size">30dp</dimen>
     <dimen name="progress_bar_height">4dp</dimen>
+    <fraction name="grid_scale_min">85%</fraction>
+    <fraction name="grid_scale_max">200%</fraction>
     <dimen name="grid_width">152dp</dimen>
-    <dimen name="grid_height">176dp</dimen>
-    <dimen name="grid_item_width">152dp</dimen>
-    <dimen name="grid_item_height">176dp</dimen>
     <dimen name="grid_section_separator_height">0dp</dimen>
     <dimen name="grid_item_margin">6dp</dimen>
     <dimen name="grid_padding_horiz">4dp</dimen>
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index 1a26bcc..86093f9 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -38,6 +38,7 @@
 import com.android.documentsui.dirlist.AnimationView.AnimationType;
 import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DocumentDetails;
+import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.files.LauncherActivity;
 import com.android.documentsui.queries.SearchViewManager;
 import com.android.documentsui.roots.LoadRootTask;
@@ -62,15 +63,18 @@
     protected final State mState;
     protected final RootsAccess mRoots;
     protected final DocumentsAccess mDocs;
+    protected final FocusHandler mFocusHandler;
     protected final SelectionManager mSelectionMgr;
     protected final SearchViewManager mSearchMgr;
     protected final Lookup<String, Executor> mExecutors;
 
+
     public AbstractActionHandler(
             T activity,
             State state,
             RootsAccess roots,
             DocumentsAccess docs,
+            FocusHandler focusHandler,
             SelectionManager selectionMgr,
             SearchViewManager searchMgr,
             Lookup<String, Executor> executors) {
@@ -78,6 +82,7 @@
         assert(activity != null);
         assert(state != null);
         assert(roots != null);
+        assert(focusHandler != null);
         assert(selectionMgr != null);
         assert(searchMgr != null);
         assert(docs != null);
@@ -86,6 +91,7 @@
         mState = state;
         mRoots = roots;
         mDocs = docs;
+        mFocusHandler = focusHandler;
         mSelectionMgr = selectionMgr;
         mSearchMgr = searchMgr;
         mExecutors = executors;
@@ -232,6 +238,16 @@
     }
 
     @Override
+    public void cutToClipboard() {
+        throw new UnsupportedOperationException("Cut not supported!");
+    }
+
+    @Override
+    public void copyToClipboard() {
+        throw new UnsupportedOperationException("Copy not supported!");
+    }
+
+    @Override
     public void deleteSelectedDocuments() {
         throw new UnsupportedOperationException("Delete not supported!");
     }
diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java
index 7f6f923..d92ebbb 100644
--- a/src/com/android/documentsui/ActionHandler.java
+++ b/src/com/android/documentsui/ActionHandler.java
@@ -65,6 +65,13 @@
 
     void openContainerDocument(DocumentInfo doc);
 
+    void cutToClipboard();
+
+    void copyToClipboard();
+
+    /**
+     * In general, selected = selection or single focused item
+     */
     void deleteSelectedDocuments();
 
     void shareSelectedDocuments();
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index f01b1a2..e9360ab 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -303,7 +303,7 @@
                 Shared.mustShowDeviceRoot(intent) || getScopedPreferences().getShowDeviceRoot();
 
         // Only show the toggle if advanced isn't forced enabled.
-        state.showAdvancedOption = !Shared.mustShowDeviceRoot(intent);
+        state.showDeviceStorageOption = !Shared.mustShowDeviceRoot(intent);
 
         if (DEBUG) Log.d(mTag, "Created new state object: " + state);
 
@@ -672,20 +672,21 @@
      * locked, open/close it as appropriate.
      */
     void toggleNavDrawerFocus() {
+        boolean toogleHappened = false;
         if (mNavDrawerHasFocus) {
             mDrawer.setOpen(false);
             DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
-            if (df != null) {
-                df.requestFocus();
-            }
+            assert (df != null);
+            toogleHappened = df.requestFocus();
         } else {
             mDrawer.setOpen(true);
             RootsFragment rf = RootsFragment.get(getFragmentManager());
-            if (rf != null) {
-                rf.requestFocus();
-            }
+            assert (rf != null);
+            toogleHappened = rf.requestFocus();
         }
-        mNavDrawerHasFocus = !mNavDrawerHasFocus;
+        if (toogleHappened) {
+            mNavDrawerHasFocus = !mNavDrawerHasFocus;
+        }
     }
 
     /**
@@ -737,7 +738,7 @@
         });
     }
 
-    public final class RetainedState {
+    public static final class RetainedState {
         public @Nullable Selection selection;
 
         public boolean hasSelection() {
diff --git a/src/com/android/documentsui/FocusManager.java b/src/com/android/documentsui/FocusManager.java
index 2371b7f..8f5d388 100644
--- a/src/com/android/documentsui/FocusManager.java
+++ b/src/com/android/documentsui/FocusManager.java
@@ -146,6 +146,16 @@
         return mScope.lastFocusPosition;
     }
 
+    @Override
+    public @Nullable String getFocusModelId() {
+        if (mScope.lastFocusPosition != RecyclerView.NO_POSITION) {
+            DocumentHolder holder = (DocumentHolder) mScope.view
+                    .findViewHolderForAdapterPosition(mScope.lastFocusPosition);
+            return holder.getModelId();
+        }
+        return null;
+    }
+
     /**
      * Finds the destination position where the focus should land for a given navigation event.
      *
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java
index e261c9f..55d2b0e 100644
--- a/src/com/android/documentsui/MenuManager.java
+++ b/src/com/android/documentsui/MenuManager.java
@@ -203,8 +203,8 @@
     }
 
     protected void updateAdvanced(MenuItem advanced) {
-        advanced.setVisible(mState.showAdvancedOption);
-        advanced.setTitle(mState.showAdvancedOption && mState.showAdvanced
+        advanced.setVisible(mState.showDeviceStorageOption);
+        advanced.setTitle(mState.showDeviceStorageOption && mState.showAdvanced
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
     }
 
diff --git a/src/com/android/documentsui/base/DebugFlags.java b/src/com/android/documentsui/base/DebugFlags.java
new file mode 100644
index 0000000..09638bb
--- /dev/null
+++ b/src/com/android/documentsui/base/DebugFlags.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+package com.android.documentsui.base;
+
+import javax.annotation.Nullable;
+
+/**
+ * Shared values that may be set by {@link DebugCommandProcessor}.
+ */
+public final class DebugFlags {
+
+    private DebugFlags() {}
+
+    private static String mQvPackage;
+    private static boolean sGestureScaleEnabled;
+    private static boolean sDocumentDetailsEnabled;
+
+    public static void setQuickViewer(@Nullable String qvPackage) {
+        mQvPackage = qvPackage;
+    }
+
+    public static @Nullable String getQuickViewer() {
+        return mQvPackage;
+    }
+
+    public static void setDocumentDetailsEnabled(boolean enabled) {
+        sDocumentDetailsEnabled = enabled;
+    }
+
+    public static boolean getDocumentDetailsEnabled() {
+        return sDocumentDetailsEnabled;
+    }
+
+    public static void setGestureScaleEnabled(boolean enabled) {
+        sGestureScaleEnabled = enabled;
+    }
+
+    public static boolean getGestureScaleEnabled() {
+        return sGestureScaleEnabled;
+    }
+}
diff --git a/src/com/android/documentsui/base/DocumentInfo.java b/src/com/android/documentsui/base/DocumentInfo.java
index f9e9b5c..afdaccc 100644
--- a/src/com/android/documentsui/base/DocumentInfo.java
+++ b/src/com/android/documentsui/base/DocumentInfo.java
@@ -31,6 +31,8 @@
 import com.android.documentsui.archives.ArchivesProvider;
 import com.android.documentsui.roots.RootCursorWrapper;
 
+import libcore.io.IoUtils;
+
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FileNotFoundException;
@@ -42,8 +44,6 @@
 
 import javax.annotation.Nullable;
 
-import libcore.io.IoUtils;
-
 /**
  * Representation of a {@link Document}.
  */
@@ -210,14 +210,15 @@
 
     @Override
     public String toString() {
-        return "Document{"
+        return "DocumentInfo{"
                 + "docId=" + documentId
                 + ", name=" + displayName
+                + ", mimeType=" + mimeType
                 + ", isContainer=" + isContainer()
                 + ", isDirectory=" + isDirectory()
                 + ", isArchive=" + isArchive()
                 + ", isPartial=" + isPartial()
-                + ", isVirtualDocument=" + isVirtualDocument()
+                + ", isVirtual=" + isVirtual()
                 + ", isDeleteSupported=" + isDeleteSupported()
                 + ", isCreateSupported=" + isCreateSupported()
                 + ", isRenameSupported=" + isRenameSupported()
@@ -228,18 +229,10 @@
         return (flags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0;
     }
 
-    public boolean isThumbnailSupported() {
-        return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
-    }
-
     public boolean isDirectory() {
         return Document.MIME_TYPE_DIR.equals(mimeType);
     }
 
-    public boolean isGridPreferred() {
-        return (flags & Document.FLAG_DIR_PREFERS_GRID) != 0;
-    }
-
     public boolean isWriteSupported() {
         return (flags & Document.FLAG_SUPPORTS_WRITE) != 0;
     }
@@ -268,7 +261,7 @@
         return isDirectory() || isArchive();
     }
 
-    public boolean isVirtualDocument() {
+    public boolean isVirtual() {
         return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
     }
 
diff --git a/src/com/android/documentsui/base/DocumentStack.java b/src/com/android/documentsui/base/DocumentStack.java
index 9e0c580..34728a7 100644
--- a/src/com/android/documentsui/base/DocumentStack.java
+++ b/src/com/android/documentsui/base/DocumentStack.java
@@ -78,11 +78,11 @@
     }
 
     /**
-     * Makes a new shallow copy, and pushes all docs to the new copy in the same order as they're
+     * Makes a new copy, and pushes all docs to the new copy in the same order as they're
      * passed as parameters, i.e. the last document will be at the top of the stack.
      */
     public DocumentStack(DocumentStack src, DocumentInfo... docs) {
-        mList = src.mList;
+        mList = new LinkedList<>(src.mList);
         for (DocumentInfo doc : docs) {
             mList.addLast(doc);
         }
diff --git a/src/com/android/documentsui/base/ScopedPreferences.java b/src/com/android/documentsui/base/ScopedPreferences.java
index e933ad0..92cca49 100644
--- a/src/com/android/documentsui/base/ScopedPreferences.java
+++ b/src/com/android/documentsui/base/ScopedPreferences.java
@@ -18,12 +18,23 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.preference.PreferenceManager;
+import android.text.TextUtils;
 
+/**
+ * Provides an interface (and runtime implementation) for preferences that are
+ * scoped (presumably to an activity). This eliminates the need to pass
+ * scoping values into {@link LocalPreferences}, as well as eliminates
+ * the static-coupling to {@link LocalPreferences} increasing testability.
+ */
 public interface ScopedPreferences {
 
     boolean getShowDeviceRoot();
     void setShowDeviceRoot(boolean display);
 
+    /**
+     * @param scope An arbitrary string representitive of the scope
+     *        for prefs that are set using this object.
+     */
     public static ScopedPreferences create(Context context, String scope) {
         return new RuntimeScopedPreferences(
                 PreferenceManager.getDefaultSharedPreferences(context), scope);
@@ -37,6 +48,8 @@
         private String mScope;
 
         private RuntimeScopedPreferences(SharedPreferences sharedPrefs, String scope)  {
+            assert(!TextUtils.isEmpty(scope));
+
             mSharedPrefs = sharedPrefs;
             mScope = scope;
         }
diff --git a/src/com/android/documentsui/base/Shared.java b/src/com/android/documentsui/base/Shared.java
index 14bb081..3fab225 100644
--- a/src/com/android/documentsui/base/Shared.java
+++ b/src/com/android/documentsui/base/Shared.java
@@ -249,7 +249,8 @@
     }
 
     /*
-     * Returns true if device root should be shown.
+     * Returns true if the local/device storage root must be visible (this also hides
+     * the option to toggle visibility in the menu.)
      */
     public static boolean mustShowDeviceRoot(Intent intent) {
         return intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
diff --git a/src/com/android/documentsui/base/State.java b/src/com/android/documentsui/base/State.java
index fff50c9..32d3cc2 100644
--- a/src/com/android/documentsui/base/State.java
+++ b/src/com/android/documentsui/base/State.java
@@ -77,7 +77,7 @@
 
     public boolean allowMultiple;
     public boolean localOnly;
-    public boolean showAdvancedOption;
+    public boolean showDeviceStorageOption;
     public boolean showAdvanced;
     public boolean restored;
     /*
@@ -127,7 +127,7 @@
         out.writeStringArray(acceptMimes);
         out.writeInt(allowMultiple ? 1 : 0);
         out.writeInt(localOnly ? 1 : 0);
-        out.writeInt(showAdvancedOption ? 1 : 0);
+        out.writeInt(showDeviceStorageOption ? 1 : 0);
         out.writeInt(showAdvanced ? 1 : 0);
         out.writeInt(restored ? 1 : 0);
         out.writeInt(external ? 1 : 0);
@@ -151,7 +151,7 @@
             state.acceptMimes = in.readStringArray();
             state.allowMultiple = in.readInt() != 0;
             state.localOnly = in.readInt() != 0;
-            state.showAdvancedOption = in.readInt() != 0;
+            state.showDeviceStorageOption = in.readInt() != 0;
             state.showAdvanced = in.readInt() != 0;
             state.restored = in.readInt() != 0;
             state.external = in.readInt() != 0;
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index b7f24f5..cc6a548 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -22,6 +22,8 @@
 import static com.android.documentsui.base.State.MODE_GRID;
 import static com.android.documentsui.base.State.MODE_LIST;
 
+import android.annotation.DimenRes;
+import android.annotation.FractionRes;
 import android.annotation.IntDef;
 import android.annotation.StringRes;
 import android.app.Activity;
@@ -36,6 +38,7 @@
 import android.content.Loader;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcelable;
@@ -66,8 +69,8 @@
 import com.android.documentsui.BaseActivity;
 import com.android.documentsui.BaseActivity.RetainedState;
 import com.android.documentsui.DirectoryLoader;
-import com.android.documentsui.DirectoryResult;
 import com.android.documentsui.DirectoryReloadLock;
+import com.android.documentsui.DirectoryResult;
 import com.android.documentsui.DocumentsApplication;
 import com.android.documentsui.FocusManager;
 import com.android.documentsui.ItemDragListener;
@@ -183,6 +186,9 @@
     private GridLayoutManager mLayout;
     private int mColumnCount = 1;  // This will get updated when layout changes.
 
+    private float mLiveScale = 1.0f;
+    private @ViewMode int mMode;
+
     private MessageBar mMessageBar;
     private View mProgressBar;
 
@@ -204,7 +210,7 @@
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 
-        BaseActivity activity = (BaseActivity<?>) getActivity();
+        BaseActivity<?> activity = (BaseActivity<?>) getActivity();
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
         mMessageBar = MessageBar.create(getChildFragmentManager());
@@ -342,6 +348,7 @@
         EventHandler<InputEvent> gestureHandler = mState.allowMultiple
                 ? gestureSel::start
                 : EventHandler.createStub(false);
+
         mInputHandler = new UserInputHandler<>(
                 mActions,
                 mFocusManager,
@@ -359,7 +366,8 @@
                 mDragStartListener::onMouseDragEvent,
                 gestureSel,
                 mInputHandler,
-                mBandController);
+                mBandController,
+                this::scaleLayout);
 
         mMenuManager = mActivity.getMenuManager();
 
@@ -497,6 +505,7 @@
      * @param mode The new view mode.
      */
     private void updateLayout(@ViewMode int mode) {
+        mMode = mode;
         mColumnCount = calculateColumnCount(mode);
         if (mLayout != null) {
             mLayout.setSpanCount(mColumnCount);
@@ -511,22 +520,65 @@
         mIconHelper.setViewMode(mode);
     }
 
+    /**
+     * Updates the layout after the view mode switches.
+     * @param mode The new view mode.
+     */
+    private void scaleLayout(float scale) {
+        assert(Build.IS_DEBUGGABLE);
+        if (DEBUG) Log.v(TAG, "Handling scale event: " + scale + ", existing scale: " + mLiveScale);
+
+        if (mMode == MODE_GRID) {
+            float minScale = getFraction(R.fraction.grid_scale_min);
+            float maxScale = getFraction(R.fraction.grid_scale_max);
+            float nextScale = mLiveScale * scale;
+
+            if (DEBUG) Log.v(TAG,
+                    "Next scale " + nextScale + ", Min/max scale " + minScale + "/" + maxScale);
+
+            if (nextScale > minScale && nextScale < maxScale) {
+                if (DEBUG) Log.d(TAG, "Updating grid scale: " + scale);
+                mLiveScale = nextScale;
+                updateLayout(mMode);
+            }
+
+        } else {
+            if (DEBUG) Log.d(TAG, "List mode, ignoring scale: " + scale);
+            mLiveScale = 1.0f;
+        }
+    }
+
     private int calculateColumnCount(@ViewMode int mode) {
         if (mode == MODE_LIST) {
             // List mode is a "grid" with 1 column.
             return 1;
         }
 
-        int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width);
-        int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin);
-        int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight();
+        int cellWidth = getScaledSize(R.dimen.grid_width);
+        int cellMargin = 2 * getScaledSize(R.dimen.grid_item_margin);
+        int viewPadding =
+                (int) ((mRecView.getPaddingLeft() + mRecView.getPaddingRight()) * mLiveScale);
 
-        // RecyclerView sometimes gets a width of 0 (see b/27150284).  Clamp so that we always lay
-        // out the grid with at least 2 columns.
+        // RecyclerView sometimes gets a width of 0 (see b/27150284).
+        // Clamp so that we always lay out the grid with at least 2 columns by default.
         int columnCount = Math.max(2,
                 (mRecView.getWidth() - viewPadding) / (cellWidth + cellMargin));
 
-        return columnCount;
+        // Finally with our grid count logic firmly in place, we apply any live scaling
+        // captured by the scale gesture detector.
+        return Math.max(1, Math.round(columnCount / mLiveScale));
+    }
+
+
+    /**
+     * Moderately abuse the "fraction" resource type for our purposes.
+     */
+    private float getFraction(@FractionRes int id) {
+        return getResources().getFraction(id, 1, 0);
+    }
+
+    private int getScaledSize(@DimenRes int id) {
+        return (int) (getResources().getDimensionPixelSize(id) * mLiveScale);
     }
 
     private int getDirectoryPadding(@ViewMode int mode) {
@@ -581,11 +633,11 @@
                 return true;
 
             case R.id.menu_cut_to_clipboard:
-                cutSelectedToClipboard();
+                mActions.cutToClipboard();
                 return true;
 
             case R.id.menu_copy_to_clipboard:
-                copySelectedToClipboard();
+                mActions.copyToClipboard();
                 return true;
 
             case R.id.menu_paste_from_clipboard:
@@ -786,34 +838,6 @@
         mRecView.requestFocus();
     }
 
-    public void copySelectedToClipboard() {
-        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_COPY_CLIPBOARD);
-
-        Selection selection = mSelectionMgr.getSelection(new Selection());
-        if (selection.isEmpty()) {
-            return;
-        }
-        mSelectionMgr.clearSelection();
-
-        mClipper.clipDocumentsForCopy(mModel::getItemUri, selection);
-
-        Snackbars.showDocumentsClipped(getActivity(), selection.size());
-    }
-
-    public void cutSelectedToClipboard() {
-        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_CUT_CLIPBOARD);
-
-        Selection selection = mSelectionMgr.getSelection(new Selection());
-        if (selection.isEmpty()) {
-            return;
-        }
-        mSelectionMgr.clearSelection();
-
-        mClipper.clipDocumentsForCut(mModel::getItemUri, selection, mState.stack.peek());
-
-        Snackbars.showDocumentsClipped(getActivity(), selection.size());
-    }
-
     public void pasteFromClipboard() {
         Metrics.logUserAction(getContext(), Metrics.USER_ACTION_PASTE_CLIPBOARD);
 
@@ -868,8 +892,12 @@
     /**
      * Attempts to restore focus on the directory listing.
      */
-    public void requestFocus() {
+    public boolean requestFocus() {
+        if (mSelectionMgr.hasSelection()) {
+            return false;
+        }
         mFocusManager.restoreLastFocus();
+        return true;
     }
 
     private void setupDragAndDropOnDocumentView(View view, Cursor cursor) {
diff --git a/src/com/android/documentsui/dirlist/DocumentHolder.java b/src/com/android/documentsui/dirlist/DocumentHolder.java
index 48a05d8..b5c1174 100644
--- a/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.graphics.Rect;
-import android.support.annotation.Nullable;
+import android.os.Build;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.KeyEvent;
@@ -28,28 +28,36 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 
-import com.android.documentsui.base.Shared;
-import com.android.documentsui.base.State;
-import com.android.documentsui.base.Events.InputEvent;
 import com.android.documentsui.R;
+import com.android.documentsui.base.DebugFlags;
+import com.android.documentsui.base.DocumentInfo;
+import com.android.documentsui.base.Events.InputEvent;
+import com.android.documentsui.base.Shared;
+import com.android.documentsui.ui.DocumentDebugInfo;
+
+import javax.annotation.Nullable;
 
 public abstract class DocumentHolder
-        extends RecyclerView.ViewHolder implements View.OnKeyListener, DocumentDetails {
+        extends RecyclerView.ViewHolder
+        implements View.OnKeyListener, DocumentDetails {
 
     static final float DISABLED_ALPHA = 0.3f;
 
-    protected @Nullable String modelId;
+    protected final Context mContext;
+    protected final @ColorInt int mDefaultBgColor;
+    protected final @ColorInt int mSelectedBgColor;
 
-    final Context mContext;
-    final @ColorInt int mDefaultBgColor;
-    final @ColorInt int mSelectedBgColor;
+    protected @Nullable String mModelId;
+
+    private final View mSelectionHotspot;
+    private final @Nullable FrameLayout mDebugContainer;
+    private @Nullable DocumentDebugInfo mDebugInfo;
 
     // See #addKeyEventListener for details on the need for this field.
-    KeyboardEventListener mKeyEventListener;
-
-    private View mSelectionHotspot;
+    private KeyboardEventListener mKeyEventListener;
 
     public DocumentHolder(Context context, ViewGroup parent, int layout) {
         this(context, inflateLayout(context, parent, layout));
@@ -64,8 +72,9 @@
 
         mDefaultBgColor = context.getColor(R.color.item_doc_background);
         mSelectedBgColor = context.getColor(R.color.item_doc_background_selected);
-
         mSelectionHotspot = itemView.findViewById(R.id.icon_check);
+
+        mDebugContainer = (FrameLayout) itemView.findViewById(R.id.debug_info);
     }
 
     /**
@@ -74,16 +83,16 @@
      * @param modelId
      * @param state
      */
-    public abstract void bind(Cursor cursor, String modelId, State state);
+    public abstract void bind(Cursor cursor, String modelId);
 
     @Override
     public boolean hasModelId() {
-        return !TextUtils.isEmpty(modelId);
+        return !TextUtils.isEmpty(mModelId);
     }
 
     @Override
     public String getModelId() {
-        return modelId;
+        return mModelId;
     }
 
     /**
@@ -158,6 +167,24 @@
         return false;
     }
 
+    protected void includeDebugInfo(DocumentInfo doc) {
+        if (mDebugContainer == null) {
+            return;
+        }
+        if (DebugFlags.getDocumentDetailsEnabled()) {
+            assert(Build.IS_DEBUGGABLE);
+            if (mDebugInfo == null) {
+                assert(mDebugContainer.getChildAt(0) == null);
+                mDebugInfo = inflateLayout(mContext, mDebugContainer, R.layout.document_debug_info);
+                mDebugContainer.addView(mDebugInfo);
+            }
+            mDebugInfo.update(doc);
+            mDebugContainer.setVisibility(View.VISIBLE);
+        } else {
+            mDebugContainer.setVisibility(View.GONE);
+        }
+    }
+
     static void setEnabledRecursive(View itemView, boolean enabled) {
         if (itemView == null || itemView.isEnabled() == enabled) {
             return;
@@ -172,9 +199,9 @@
         }
     }
 
-    private static View inflateLayout(Context context, ViewGroup parent, int layout) {
+    private static <V extends View> V inflateLayout(Context context, ViewGroup parent, int layout) {
         final LayoutInflater inflater = LayoutInflater.from(context);
-        return inflater.inflate(layout, parent, false);
+        return (V) inflater.inflate(layout, parent, false);
     }
 
     static ViewPropertyAnimator fade(ImageView view, float alpha) {
diff --git a/src/com/android/documentsui/dirlist/FocusHandler.java b/src/com/android/documentsui/dirlist/FocusHandler.java
index 0f43ac0..1cbb8a9 100644
--- a/src/com/android/documentsui/dirlist/FocusHandler.java
+++ b/src/com/android/documentsui/dirlist/FocusHandler.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui.dirlist;
 
+import android.annotation.Nullable;
 import android.view.KeyEvent;
 import android.view.View;
 
@@ -52,4 +53,8 @@
      */
     int getFocusPosition();
 
+    /**
+     * @return The modelId of the last focused item. If no item is focused, this should return null.
+     */
+    @Nullable String getFocusModelId();
 }
diff --git a/src/com/android/documentsui/dirlist/GridDirectoryHolder.java b/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
index 962f6bc..fb6c3c3 100644
--- a/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
+++ b/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
@@ -26,8 +26,10 @@
 import android.widget.TextView;
 
 import com.android.documentsui.R;
-import com.android.documentsui.base.State;
+import com.android.documentsui.base.DebugFlags;
+import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.Events.InputEvent;
+import com.android.documentsui.roots.RootCursorWrapper;
 
 final class GridDirectoryHolder extends DocumentHolder {
 
@@ -71,13 +73,18 @@
      * @param state Current display state.
      */
     @Override
-    public void bind(Cursor cursor, String modelId, State state) {
+    public void bind(Cursor cursor, String modelId) {
         assert(cursor != null);
 
-        this.modelId = modelId;
+        this.mModelId = modelId;
 
-        final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
-        mTitle.setText(docDisplayName, TextView.BufferType.SPANNABLE);
+        mTitle.setText(
+                getCursorString(cursor, Document.COLUMN_DISPLAY_NAME),
+                TextView.BufferType.SPANNABLE);
 
+        if (DebugFlags.getDocumentDetailsEnabled()) {
+            String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
+            includeDebugInfo(DocumentInfo.fromCursor(cursor, authority));
+        }
     }
 }
diff --git a/src/com/android/documentsui/dirlist/GridDocumentHolder.java b/src/com/android/documentsui/dirlist/GridDocumentHolder.java
index 051114d..f4f7d7c 100644
--- a/src/com/android/documentsui/dirlist/GridDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/GridDocumentHolder.java
@@ -16,15 +16,12 @@
 
 package com.android.documentsui.dirlist;
 
-import static com.android.documentsui.base.DocumentInfo.getCursorInt;
 import static com.android.documentsui.base.DocumentInfo.getCursorLong;
 import static com.android.documentsui.base.DocumentInfo.getCursorString;
 
 import android.annotation.ColorInt;
 import android.content.Context;
 import android.database.Cursor;
-import android.net.Uri;
-import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.text.format.Formatter;
 import android.view.View;
@@ -33,15 +30,14 @@
 import android.widget.TextView;
 
 import com.android.documentsui.R;
-import com.android.documentsui.base.Shared;
-import com.android.documentsui.base.State;
+import com.android.documentsui.base.DebugFlags;
+import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.Events.InputEvent;
+import com.android.documentsui.base.Shared;
 import com.android.documentsui.roots.RootCursorWrapper;
 
 final class GridDocumentHolder extends DocumentHolder {
 
-    private static boolean mHideTitles;
-
     final TextView mTitle;
     final TextView mDate;
     final TextView mDetails;
@@ -52,6 +48,8 @@
     final IconHelper mIconHelper;
 
     private final @ColorInt int mDisabledBgColor;
+    // This is used in as a convenience in our bind method.
+    private final DocumentInfo mDoc = new DocumentInfo();
 
     public GridDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
         super(context, parent, R.layout.item_doc_grid);
@@ -123,18 +121,12 @@
      * @param state Current display state.
      */
     @Override
-    public void bind(Cursor cursor, String modelId, State state) {
+    public void bind(Cursor cursor, String modelId) {
         assert(cursor != null);
 
-        this.modelId = modelId;
+        mModelId = modelId;
 
-        final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
-        final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
-        final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
-        final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
-        final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
-        final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
-        final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+        mDoc.updateFromCursor(cursor, getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY));
 
         mIconHelper.stopLoading(mIconThumb);
 
@@ -143,38 +135,36 @@
         mIconThumb.animate().cancel();
         mIconThumb.setAlpha(0f);
 
-        final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
-        mIconHelper.load(uri, docMimeType, docFlags, docIcon, docLastModified, mIconThumb,
-                mIconMimeLg, mIconMimeSm);
+        mIconHelper.load(mDoc, mIconThumb, mIconMimeLg, mIconMimeSm);
 
-        if (mHideTitles) {
-            mTitle.setVisibility(View.GONE);
-        } else {
-            mTitle.setText(docDisplayName, TextView.BufferType.SPANNABLE);
-            mTitle.setVisibility(View.VISIBLE);
-        }
+        mTitle.setText(mDoc.displayName, TextView.BufferType.SPANNABLE);
+        mTitle.setVisibility(View.VISIBLE);
 
         // If file is partial, we want to show summary field as that's more relevant than fileSize
         // and date
-        if ((docFlags & Document.FLAG_PARTIAL) != 0) {
+        if (mDoc.isPartial()) {
             final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
             mDetails.setVisibility(View.VISIBLE);
             mDate.setText(null);
             mDetails.setText(docSummary);
         } else {
-            if (docLastModified == -1) {
+            if (mDoc.lastModified == -1) {
                 mDate.setText(null);
             } else {
-                mDate.setText(Shared.formatTime(mContext, docLastModified));
+                mDate.setText(Shared.formatTime(mContext, mDoc.lastModified));
             }
 
             final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
-            if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
+            if (mDoc.isDirectory() || docSize == -1) {
                 mDetails.setVisibility(View.GONE);
             } else {
                 mDetails.setVisibility(View.VISIBLE);
                 mDetails.setText(Formatter.formatFileSize(mContext, docSize));
             }
         }
+
+        if (DebugFlags.getDocumentDetailsEnabled()) {
+            includeDebugInfo(mDoc);
+        }
     }
 }
diff --git a/src/com/android/documentsui/dirlist/IconHelper.java b/src/com/android/documentsui/dirlist/IconHelper.java
index 6f6327b..fa1c121 100644
--- a/src/com/android/documentsui/dirlist/IconHelper.java
+++ b/src/com/android/documentsui/dirlist/IconHelper.java
@@ -215,6 +215,24 @@
     /**
      * Load thumbnails for a directory list item.
      *
+     * @param doc The document
+     * @param iconThumb The itemview's thumbnail icon.
+     * @param iconMime The itemview's mime icon. Hidden when iconThumb is shown.
+     * @param subIconMime The second itemview's mime icon. Always visible.
+     * @return
+     */
+    public void load(
+            DocumentInfo doc,
+            ImageView iconThumb,
+            ImageView iconMime,
+            @Nullable ImageView subIconMime) {
+        load(doc.derivedUri, doc.mimeType, doc.flags, doc.icon, doc.lastModified,
+                iconThumb, iconMime, subIconMime);
+    }
+
+    /**
+     * Load thumbnails for a directory list item.
+     *
      * @param uri The URI for the file being represented.
      * @param mimeType The mime type of the file being represented.
      * @param docFlags Flags for the file being represented.
diff --git a/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/src/com/android/documentsui/dirlist/ListDocumentHolder.java
index 8921489..c65839f 100644
--- a/src/com/android/documentsui/dirlist/ListDocumentHolder.java
+++ b/src/com/android/documentsui/dirlist/ListDocumentHolder.java
@@ -16,16 +16,11 @@
 
 package com.android.documentsui.dirlist;
 
-import static com.android.documentsui.base.DocumentInfo.getCursorInt;
-import static com.android.documentsui.base.DocumentInfo.getCursorLong;
 import static com.android.documentsui.base.DocumentInfo.getCursorString;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.graphics.Rect;
-import android.net.Uri;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
 import android.text.format.Formatter;
 import android.view.View;
 import android.view.ViewGroup;
@@ -34,22 +29,25 @@
 import android.widget.TextView;
 
 import com.android.documentsui.R;
-import com.android.documentsui.base.Shared;
-import com.android.documentsui.base.State;
+import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.Events.InputEvent;
+import com.android.documentsui.base.Shared;
 import com.android.documentsui.roots.RootCursorWrapper;
 
 final class ListDocumentHolder extends DocumentHolder {
-    final TextView mTitle;
-    final LinearLayout mDetails;  // Container of date/size/summary
-    final TextView mDate;
-    final TextView mSize;
-    final TextView mSummary;
-    final ImageView mIconMime;
-    final ImageView mIconThumb;
-    final ImageView mIconCheck;
-    final IconHelper mIconHelper;
-    final View mIconLayout;
+
+    private final TextView mTitle;
+    private final LinearLayout mDetails;  // Container of date/size/summary
+    private final TextView mDate;
+    private final TextView mSize;
+    private final TextView mSummary;
+    private final ImageView mIconMime;
+    private final ImageView mIconThumb;
+    private final ImageView mIconCheck;
+    private final IconHelper mIconHelper;
+    private final View mIconLayout;
+    // This is used in as a convenience in our bind method.
+    private final DocumentInfo mDoc;
 
     public ListDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
         super(context, parent, R.layout.item_doc_list);
@@ -66,6 +64,7 @@
         mDetails = (LinearLayout) itemView.findViewById(R.id.line2);
 
         mIconHelper = iconHelper;
+        mDoc = new DocumentInfo();
     }
 
     @Override
@@ -138,21 +137,12 @@
      * @param state Current display state.
      */
     @Override
-    public void bind(Cursor cursor, String modelId, State state) {
+    public void bind(Cursor cursor, String modelId) {
         assert(cursor != null);
 
-        this.modelId = modelId;
+        mModelId = modelId;
 
-        final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
-        final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
-        final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
-        final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
-        final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
-        final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
-        final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
-        final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
-        final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
-        final boolean isDirectory = Document.MIME_TYPE_DIR.equals(docMimeType);
+        mDoc.updateFromCursor(cursor, getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY));
 
         mIconHelper.stopLoading(mIconThumb);
 
@@ -161,38 +151,36 @@
         mIconThumb.animate().cancel();
         mIconThumb.setAlpha(0f);
 
-        final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
-        mIconHelper.load(uri, docMimeType, docFlags, docIcon, docLastModified, mIconThumb,
-                mIconMime, null);
+        mIconHelper.load(mDoc, mIconThumb, mIconMime, null);
 
-        mTitle.setText(docDisplayName, TextView.BufferType.SPANNABLE);
+        mTitle.setText(mDoc.displayName, TextView.BufferType.SPANNABLE);
         mTitle.setVisibility(View.VISIBLE);
 
 
         boolean hasDetails = false;
-        if (isDirectory) {
+        if (mDoc.isDirectory()) {
             // Note, we don't show any details for any directory...ever.
             hasDetails = false;
         } else {
-            if (docSummary != null) {
+            if (mDoc.summary != null) {
                 hasDetails = true;
-                mSummary.setText(docSummary);
+                mSummary.setText(mDoc.summary);
                 mSummary.setVisibility(View.VISIBLE);
             } else {
                 mSummary.setVisibility(View.INVISIBLE);
             }
 
-            if (docLastModified > 0) {
+            if (mDoc.lastModified > 0) {
                 hasDetails = true;
-                mDate.setText(Shared.formatTime(mContext, docLastModified));
+                mDate.setText(Shared.formatTime(mContext, mDoc.lastModified));
             } else {
                 mDate.setText(null);
             }
 
-            if (docSize > -1) {
+            if (mDoc.size > -1) {
                 hasDetails = true;
                 mSize.setVisibility(View.VISIBLE);
-                mSize.setText(Formatter.formatFileSize(mContext, docSize));
+                mSize.setText(Formatter.formatFileSize(mContext, mDoc.size));
             } else {
                 mSize.setVisibility(View.GONE);
             }
@@ -202,5 +190,8 @@
         if (mDetails != null) {
             mDetails.setVisibility(hasDetails ? View.VISIBLE : View.GONE);
         }
+
+        // TODO: Add document debug info
+        // Call includeDebugInfo
     }
 }
diff --git a/src/com/android/documentsui/dirlist/ListeningGestureDetector.java b/src/com/android/documentsui/dirlist/ListeningGestureDetector.java
index 296fa70..17ac867 100644
--- a/src/com/android/documentsui/dirlist/ListeningGestureDetector.java
+++ b/src/com/android/documentsui/dirlist/ListeningGestureDetector.java
@@ -16,15 +16,21 @@
 
 package com.android.documentsui.dirlist;
 
+import static com.android.documentsui.base.Shared.DEBUG;
+
 import android.annotation.Nullable;
 import android.content.Context;
+import android.os.Build;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.OnItemTouchListener;
+import android.util.Log;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
 import android.view.View;
 import android.view.View.OnTouchListener;
 
+import com.android.documentsui.base.DebugFlags;
 import com.android.documentsui.base.EventHandler;
 import com.android.documentsui.base.Events;
 import com.android.documentsui.base.Events.InputEvent;
@@ -32,18 +38,25 @@
 import com.android.documentsui.selection.BandController;
 import com.android.documentsui.selection.GestureSelector;
 
+import java.util.function.Consumer;
+
 //Receives event meant for both directory and empty view, and either pass them to
 //{@link UserInputHandler} for simple gestures (Single Tap, Long-Press), or intercept them for
 //other types of gestures (drag n' drop)
 final class ListeningGestureDetector extends GestureDetector
         implements OnItemTouchListener, OnTouchListener {
 
+    private static final String TAG = "ListeningGestureDetector";
+
     private final GestureSelector mGestureSelector;
     private final EventHandler<InputEvent> mMouseDragListener;
     private final BandController mBandController;
     private final MouseDelegate mMouseDelegate = new MouseDelegate();
     private final TouchDelegate mTouchDelegate = new TouchDelegate();
 
+    // Currently only initialized on IS_DEBUGGABLE builds.
+    private final @Nullable ScaleGestureDetector mScaleDetector;
+
     public ListeningGestureDetector(
             Context context,
             RecyclerView recView,
@@ -51,19 +64,45 @@
             EventHandler<InputEvent> mouseDragListener,
             GestureSelector gestureSelector,
             UserInputHandler<? extends InputEvent> handler,
-            @Nullable BandController bandController) {
+            @Nullable BandController bandController,
+            Consumer<Float> scaleHandler) {
+
         super(context, handler);
+
         mMouseDragListener = mouseDragListener;
         mGestureSelector = gestureSelector;
         mBandController = bandController;
         recView.addOnItemTouchListener(this);
         emptyView.setOnTouchListener(this);
+
+        mScaleDetector = !Build.IS_DEBUGGABLE
+                ? null
+                : new ScaleGestureDetector(
+                        context,
+                        new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+                            @Override
+                            public boolean onScale(ScaleGestureDetector detector) {
+                                if (DEBUG) Log.v(TAG,
+                                        "Received scale event: " + detector.getScaleFactor());
+                                scaleHandler.accept(detector.getScaleFactor());
+                                return true;
+                            }
+                        });
     }
 
     @Override
     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
         boolean handled = false;
 
+        // This is an in-development feature.
+        // TODO: Re-wire event handling so that we're not dispatching
+        //     events to to scaledetector's #onTouchEvent from this
+        //     #onInterceptTouchEvent touch event.
+        if (DebugFlags.getGestureScaleEnabled()
+                && mScaleDetector != null) {
+            mScaleDetector.onTouchEvent(e);
+        }
+
         try (InputEvent event = MotionInputEvent.obtain(e, rv)) {
             if (event.isMouseEvent()) {
                 handled |= mMouseDelegate.onInterceptTouchEvent(event);
diff --git a/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
index adc4b04..99f9371 100644
--- a/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
+++ b/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
@@ -129,7 +129,7 @@
     public void onBindViewHolder(DocumentHolder holder, int position) {
         String modelId = mModelIds.get(position);
         Cursor cursor = mEnv.getModel().getItem(modelId);
-        holder.bind(cursor, modelId, mEnv.getDisplayState());
+        holder.bind(cursor, modelId);
 
         final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
         final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
diff --git a/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java b/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
index 69f9566..4cb55b3 100644
--- a/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
+++ b/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
@@ -240,6 +240,7 @@
      */
     private static final class EmptyDocumentHolder extends DocumentHolder {
         final int mVisibleHeight;
+        private State mState;
 
         public EmptyDocumentHolder(Context context) {
             super(context, new Space(context));
@@ -249,12 +250,13 @@
         }
 
         public void bind(State state) {
-            bind(null, null, state);
+            mState = state;
+            bind(null, null);
         }
 
         @Override
-        public void bind(Cursor cursor, String modelId, State state) {
-            if (state.derivedMode == State.MODE_GRID) {
+        public void bind(Cursor cursor, String modelId) {
+            if (mState.derivedMode == State.MODE_GRID) {
                 itemView.setMinimumHeight(mVisibleHeight);
             } else {
                 itemView.setMinimumHeight(0);
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index afea4cd..09f5ce1 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -48,6 +48,7 @@
 import com.android.documentsui.clipping.UrisSupplier;
 import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DocumentDetails;
+import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.dirlist.Model.Update;
 import com.android.documentsui.files.ActionHandler.Addons;
@@ -87,6 +88,7 @@
             State state,
             RootsAccess roots,
             DocumentsAccess docs,
+            FocusHandler focusHandler,
             SelectionManager selectionMgr,
             SearchViewManager searchMgr,
             Lookup<String, Executor> executors,
@@ -96,7 +98,7 @@
             DocumentClipper clipper,
             ClipStore clipStore) {
 
-        super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
+        super(activity, state, roots, docs, focusHandler, selectionMgr, searchMgr, executors);
 
         mActionModeAddons = actionModeAddons;
         mDialogs = dialogs;
@@ -190,14 +192,57 @@
         return previewDocument(doc);
     }
 
+    private Selection getSelectedOrFocused() {
+        final Selection selection = this.getStableSelection();
+        if (selection.isEmpty()) {
+            String focusModelId = mFocusHandler.getFocusModelId();
+            if (focusModelId != null) {
+                selection.add(focusModelId);
+            }
+        }
+
+        return selection;
+    }
+
+    @Override
+    public void cutToClipboard() {
+        Metrics.logUserAction(mActivity, Metrics.USER_ACTION_CUT_CLIPBOARD);
+        Selection selection = getSelectedOrFocused();
+
+        if (selection.isEmpty()) {
+            return;
+        }
+        mSelectionMgr.clearSelection();
+
+        mClipper.clipDocumentsForCut(mScope.model::getItemUri, selection, mState.stack.peek());
+
+        mDialogs.showDocumentsClipped(selection.size());
+    }
+
+    @Override
+    public void copyToClipboard() {
+        Metrics.logUserAction(mActivity, Metrics.USER_ACTION_COPY_CLIPBOARD);
+        Selection selection = getSelectedOrFocused();
+
+        if (selection.isEmpty()) {
+            return;
+        }
+        mSelectionMgr.clearSelection();
+
+        mClipper.clipDocumentsForCopy(mScope.model::getItemUri, selection);
+
+        mDialogs.showDocumentsClipped(selection.size());
+    }
+
+
     @Override
     public void deleteSelectedDocuments() {
-        assert(mSelectionMgr.hasSelection());
-
         Metrics.logUserAction(mActivity, Metrics.USER_ACTION_DELETE);
+        Selection selection = getSelectedOrFocused();
 
-        Selection selection = getStableSelection();
-        assert(!selection.isEmpty());
+        if (selection.isEmpty()) {
+            return;
+        }
 
         final DocumentInfo srcParent = mState.stack.peek();
         assert(srcParent != null);
diff --git a/src/com/android/documentsui/files/ActivityInputHandler.java b/src/com/android/documentsui/files/ActivityInputHandler.java
index a7b0e2c..35653d2 100644
--- a/src/com/android/documentsui/files/ActivityInputHandler.java
+++ b/src/com/android/documentsui/files/ActivityInputHandler.java
@@ -18,31 +18,22 @@
 
 import android.view.KeyEvent;
 
-import com.android.documentsui.selection.SelectionManager;
-import com.android.documentsui.ActionHandler;
-
 /**
  * Used by {@link FilesActivity} to manage global keyboard shortcuts tied to file actions
  */
 final class ActivityInputHandler {
 
-    private final SelectionManager mSelectionMgr;
-    private final ActionHandler mActions;
+    private final Runnable mDeleteHandler;
 
-    ActivityInputHandler(SelectionManager selectionMgr, ActionHandler actionHandler) {
-        mSelectionMgr = selectionMgr;
-        mActions = actionHandler;
+    ActivityInputHandler(Runnable deleteHandler) {
+        mDeleteHandler = deleteHandler;
     }
 
     boolean onKeyDown(int keyCode, KeyEvent event) {
         if ((keyCode == KeyEvent.KEYCODE_DEL && event.isAltPressed())
                 || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
-            if (mSelectionMgr.hasSelection()) {
-                mActions.deleteSelectedDocuments();
-                return true;
-            } else {
-                return false;
-            }
+            mDeleteHandler.run();
+            return true;
         }
         return false;
     }
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 3eecdca..a62e6af 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -56,6 +56,7 @@
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.dirlist.DocumentsAdapter;
 import com.android.documentsui.dirlist.Model;
+import com.android.documentsui.selection.Selection;
 import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.selection.SelectionManager.SelectionPredicate;
 import com.android.documentsui.services.FileOperationService;
@@ -124,6 +125,7 @@
                 mState,
                 mRoots,
                 mDocs,
+                mFocusManager,
                 mSelectionMgr,
                 mSearchManager,
                 ProviderExecutor::forAuthority,
@@ -133,7 +135,7 @@
                 mClipper,
                 DocumentsApplication.getClipStore(this));
 
-        mActivityInputHandler = new ActivityInputHandler(mSelectionMgr, mActions);
+        mActivityInputHandler = new ActivityInputHandler(mActions::deleteSelectedDocuments);
 
         RootsFragment.show(getFragmentManager(), null);
 
@@ -324,16 +326,10 @@
                 }
                 return true;
             case KeyEvent.KEYCODE_X:
-                dir = getDirectoryFragment();
-                if (dir != null) {
-                    dir.cutSelectedToClipboard();
-                }
+                mActions.cutToClipboard();
                 return true;
             case KeyEvent.KEYCODE_C:
-                dir = getDirectoryFragment();
-                if (dir != null) {
-                    dir.copySelectedToClipboard();
-                }
+                mActions.copyToClipboard();
                 return true;
             case KeyEvent.KEYCODE_V:
                 dir = getDirectoryFragment();
diff --git a/src/com/android/documentsui/files/QuickViewIntentBuilder.java b/src/com/android/documentsui/files/QuickViewIntentBuilder.java
index 3304cc1..c85af8a 100644
--- a/src/com/android/documentsui/files/QuickViewIntentBuilder.java
+++ b/src/com/android/documentsui/files/QuickViewIntentBuilder.java
@@ -36,9 +36,9 @@
 import android.util.Range;
 
 import com.android.documentsui.R;
+import com.android.documentsui.base.DebugFlags;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.dirlist.Model;
-import com.android.documentsui.queries.SetQuickViewerCommand;
 import com.android.documentsui.roots.RootCursorWrapper;
 
 import java.util.ArrayList;
@@ -142,8 +142,9 @@
         // Allow users of debug devices to override default quick viewer
         // for the purposes of testing.
         if (Build.IS_DEBUGGABLE) {
-            if (SetQuickViewerCommand.sQuickViewer != null) {
-                return SetQuickViewerCommand.sQuickViewer;
+            String quickViewer = DebugFlags.getQuickViewer();
+            if (quickViewer != null) {
+                return quickViewer;
             }
             return android.os.SystemProperties.get("debug.quick_viewer", resValue);
         }
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index 6e1b9d4..1999381 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -40,6 +40,7 @@
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
 import com.android.documentsui.dirlist.DocumentDetails;
+import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.dirlist.Model.Update;
 import com.android.documentsui.picker.ActionHandler.Addons;
@@ -66,12 +67,13 @@
             State state,
             RootsAccess roots,
             DocumentsAccess docs,
+            FocusHandler focusHandler,
             SelectionManager selectionMgr,
             SearchViewManager searchMgr,
             Lookup<String, Executor> executors,
             ActivityConfig activityConfig) {
 
-        super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
+        super(activity, state, roots, docs, focusHandler, selectionMgr, searchMgr, executors);
 
         mConfig = activityConfig;
         mScope = new ContentScope(this::onModelLoaded);
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 268d801..eee92e5 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -110,6 +110,7 @@
                 mState,
                 mRoots,
                 mDocs,
+                mFocusManager,
                 mSelectionMgr,
                 mSearchManager,
                 ProviderExecutor::forAuthority,
diff --git a/src/com/android/documentsui/queries/DebugCommandProcessor.java b/src/com/android/documentsui/queries/DebugCommandProcessor.java
index 70d9d64..7a12a18 100644
--- a/src/com/android/documentsui/queries/DebugCommandProcessor.java
+++ b/src/com/android/documentsui/queries/DebugCommandProcessor.java
@@ -17,8 +17,10 @@
 
 import android.os.Build;
 import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.documentsui.base.DebugFlags;
 import com.android.documentsui.base.EventHandler;
 
 import java.util.ArrayList;
@@ -26,11 +28,18 @@
 
 final class DebugCommandProcessor implements EventHandler<String> {
 
+    @VisibleForTesting
+    static final String COMMAND_PREFIX = "dbg:";
+
+    private static final String TAG = "DebugCommandProcessor";
+
     private final List<EventHandler<String[]>> mCommands = new ArrayList<>();
 
     public DebugCommandProcessor() {
         if (Build.IS_DEBUGGABLE) {
-            mCommands.add(new SetQuickViewerCommand());
+            mCommands.add(DebugCommandProcessor::quickViewer);
+            mCommands.add(DebugCommandProcessor::gestureScale);
+            mCommands.add(DebugCommandProcessor::docDetails);
         }
     }
 
@@ -43,8 +52,8 @@
 
     @Override
     public boolean accept(String query) {
-        if (query.length() > 6 && query.substring(0, 6).equals("#debug")) {
-            String[] tokens = query.substring(7).split("\\s+");
+        if (query.length() > COMMAND_PREFIX.length() && query.startsWith(COMMAND_PREFIX)) {
+            String[] tokens = query.substring(COMMAND_PREFIX.length()).split("\\s+");
             for (EventHandler<String[]> command : mCommands) {
                 if (command.accept(tokens)) {
                     return true;
@@ -54,4 +63,57 @@
         }
         return false;
     }
+
+    private static boolean quickViewer(String[] tokens) {
+        if ("qv".equals(tokens[0])) {
+            if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
+                DebugFlags.setQuickViewer(tokens[1]);
+                Log.i(TAG, "Set quick viewer to: " + tokens[1]);
+                return true;
+            } else {
+                Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
+            }
+        } else if ("deqv".equals(tokens[0])) {
+            Log.i(TAG, "Unset quick viewer");
+            DebugFlags.setQuickViewer(null);
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean gestureScale(String[] tokens) {
+        if ("gs".equals(tokens[0])) {
+            if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
+                boolean enabled = asBool(tokens[1]);
+                DebugFlags.setGestureScaleEnabled(enabled);
+                Log.i(TAG, "Set gesture scale enabled to: " + enabled);
+                return true;
+            }
+            Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
+        }
+        return false;
+    }
+
+    private static boolean docDetails(String[] tokens) {
+        if ("docinfo".equals(tokens[0])) {
+            if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
+                boolean enabled = asBool(tokens[1]);
+                DebugFlags.setDocumentDetailsEnabled(enabled);
+                Log.i(TAG, "Set gesture scale enabled to: " + enabled);
+                return true;
+            }
+            Log.w(TAG, "Invalid command structure: " + TextUtils.join(" ", tokens));
+        }
+        return false;
+    }
+
+    private static final boolean asBool(String val) {
+        if (val == null || val.equals("0")) {
+            return false;
+        }
+        if (val.equals("1")) {
+            return true;
+        }
+        return Boolean.valueOf(val);
+    }
 }
diff --git a/src/com/android/documentsui/queries/SetQuickViewerCommand.java b/src/com/android/documentsui/queries/SetQuickViewerCommand.java
deleted file mode 100644
index 37fe0db..0000000
--- a/src/com/android/documentsui/queries/SetQuickViewerCommand.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-package com.android.documentsui.queries;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.documentsui.base.EventHandler;
-
-public class SetQuickViewerCommand implements EventHandler<String[]> {
-
-    // This is a quick/easy shortcut to sharing quick viewer debug settings
-    // with QuickViewIntent builder. Tried setting at a system property
-    // but got a native error. This being quick and easy, didn't investigate that err.
-    public static String sQuickViewer;
-    private static final String TAG = "SetQuickViewerCommand";
-
-    @Override
-    public boolean accept(String[] tokens) {
-        if ("setqv".equals(tokens[0])) {
-            if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
-                sQuickViewer = tokens[1];
-                Log.i(TAG, "Set quick viewer to: " + sQuickViewer);
-                return true;
-            } else {
-                Log.w(TAG, "Invalid command structure: " + tokens);
-            }
-        } else if ("unsetqv".equals(tokens[0])) {
-            Log.i(TAG, "Unset quick viewer");
-            sQuickViewer = null;
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/documentsui/services/CopyJob.java b/src/com/android/documentsui/services/CopyJob.java
index b9bc651..30bbbf1 100644
--- a/src/com/android/documentsui/services/CopyJob.java
+++ b/src/com/android/documentsui/services/CopyJob.java
@@ -401,7 +401,7 @@
         if (DEBUG) Log.d(TAG, "Doing byte copy of document: " + src);
         // If the file is virtual, but can be converted to another format, then try to copy it
         // as such format. Also, append an extension for the target mime type (if known).
-        if (src.isVirtualDocument()) {
+        if (src.isVirtual()) {
             String[] streamTypes = null;
             try {
                 streamTypes = getContentResolver().getStreamTypes(src.derivedUri, "*/*");
@@ -538,7 +538,7 @@
         try {
             // If the file is virtual, but can be converted to another format, then try to copy it
             // as such format.
-            if (src.isVirtualDocument()) {
+            if (src.isVirtual()) {
                 try {
                     srcFileAsAsset = getClient(src).openTypedAssetFileDescriptor(
                                 src.derivedUri, mimeType, null, canceller);
@@ -592,7 +592,7 @@
                         src.derivedUri, dest.derivedUri, e);
             }
 
-            if (src.isVirtualDocument()) {
+            if (src.isVirtual()) {
                convertedFiles.add(src);
             }
 
diff --git a/src/com/android/documentsui/services/MoveJob.java b/src/com/android/documentsui/services/MoveJob.java
index 16bda15..7e66cb0 100644
--- a/src/com/android/documentsui/services/MoveJob.java
+++ b/src/com/android/documentsui/services/MoveJob.java
@@ -154,7 +154,7 @@
         // Moving virtual files by bytes is not supported. This is because, it would involve
         // conversion, and the source file should not be deleted in such case (as it's a different
         // file).
-        if (src.isVirtualDocument()) {
+        if (src.isVirtual()) {
             throw new ResourceException("Cannot move virtual file %s byte by byte.",
                     src.derivedUri);
         }
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index 8890fb3..4e9287e 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -357,8 +357,8 @@
     /**
      * Attempts to shift focus back to the navigation drawer.
      */
-    public void requestFocus() {
-        mList.requestFocus();
+    public boolean requestFocus() {
+        return mList.requestFocus();
     }
 
     private BaseActivity<?> getBaseActivity() {
diff --git a/src/com/android/documentsui/ui/DialogController.java b/src/com/android/documentsui/ui/DialogController.java
index 6c648a2..39bebfe 100644
--- a/src/com/android/documentsui/ui/DialogController.java
+++ b/src/com/android/documentsui/ui/DialogController.java
@@ -50,11 +50,17 @@
         public void showNoApplicationFound() {
             throw new UnsupportedOperationException();
         }
+
+        @Override
+        public void showDocumentsClipped(int size) {
+            throw new UnsupportedOperationException();
+        }
     };
 
     void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback);
     void showFileOperationFailures(int status, int opType, int docCount);
     void showNoApplicationFound();
+    void showDocumentsClipped(int size);
 
     // Should be private, but Java doesn't like me treating an interface like a mini-package.
     public static final class RuntimeDialogController implements DialogController {
@@ -137,6 +143,11 @@
             Snackbars.makeSnackbar(
                     mActivity, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
         }
+
+        @Override
+        public void showDocumentsClipped(int size) {
+            Snackbars.showDocumentsClipped(mActivity, size);
+        }
     }
 
     static DialogController create(Activity activity, MessageBuilder messages) {
diff --git a/src/com/android/documentsui/ui/DocumentDebugInfo.java b/src/com/android/documentsui/ui/DocumentDebugInfo.java
new file mode 100644
index 0000000..18d66f2
--- /dev/null
+++ b/src/com/android/documentsui/ui/DocumentDebugInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.documentsui.ui;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.documentsui.base.DocumentInfo;
+
+/**
+ * Document debug info view.
+ */
+public class DocumentDebugInfo extends TextView {
+    public DocumentDebugInfo(Context context) {
+        super(context);
+
+    }
+
+    public DocumentDebugInfo(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void update(DocumentInfo doc) {
+
+        String dbgInfo = new StringBuilder()
+                .append("** PROPERTIES **\n\n")
+                .append("docid: " + doc.documentId).append("\n")
+                .append("name: " + doc.displayName).append("\n")
+                .append("mimetype: " + doc.mimeType).append("\n")
+                .append("container: " + doc.isContainer()).append("\n")
+                .append("virtual: " + doc.isVirtual()).append("\n")
+                .append("\n")
+                .append("** OPERATIONS **\n\n")
+                .append("create: " + doc.isCreateSupported()).append("\n")
+                .append("delete: " + doc.isDeleteSupported()).append("\n")
+                .append("rename: " + doc.isRenameSupported()).append("\n")
+                .toString();
+
+        setText(dbgInfo);
+    }
+}
diff --git a/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java b/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java
index 45e0655..76db4e1 100644
--- a/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java
+++ b/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java
@@ -45,6 +45,11 @@
     }
 
     @Override
+    public String getFocusModelId() {
+        return null;
+    }
+
+    @Override
     public void focusDocument(String modelId) {
     }
 
diff --git a/tests/common/com/android/documentsui/testing/TestActionHandler.java b/tests/common/com/android/documentsui/testing/TestActionHandler.java
index 69f716c..bd8adac 100644
--- a/tests/common/com/android/documentsui/testing/TestActionHandler.java
+++ b/tests/common/com/android/documentsui/testing/TestActionHandler.java
@@ -40,6 +40,7 @@
                 env.state,
                 env.roots,
                 env.docs,
+                env.focusHandler,
                 env.selectionMgr,
                 env.searchViewManager,
                 (String authority) -> null);
diff --git a/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java b/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java
index 6ffdf70..f94f33f 100644
--- a/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java
+++ b/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java
@@ -48,4 +48,8 @@
     public void assertCalled() {
         Assert.assertTrue(mCalled);
     }
+
+    public void assertNeverCalled() {
+        Assert.assertFalse(mCalled);
+    }
 }
diff --git a/tests/common/com/android/documentsui/testing/TestEnv.java b/tests/common/com/android/documentsui/testing/TestEnv.java
index e0af394..076e621 100644
--- a/tests/common/com/android/documentsui/testing/TestEnv.java
+++ b/tests/common/com/android/documentsui/testing/TestEnv.java
@@ -21,6 +21,7 @@
 
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.State;
+import com.android.documentsui.dirlist.TestFocusHandler;
 import com.android.documentsui.dirlist.TestModel;
 import com.android.documentsui.selection.SelectionManager;
 
@@ -50,6 +51,7 @@
     public final State state = new State();
     public final TestRootsAccess roots = new TestRootsAccess();
     public final TestDocumentsAccess docs = new TestDocumentsAccess();
+    public final TestFocusHandler focusHandler = new TestFocusHandler();
     public final TestModel model;
     public final SelectionManager selectionMgr;
     public final TestSearchViewManager searchViewManager;
diff --git a/tests/common/com/android/documentsui/ui/TestDialogController.java b/tests/common/com/android/documentsui/ui/TestDialogController.java
index dbac0f5..e3135be 100644
--- a/tests/common/com/android/documentsui/ui/TestDialogController.java
+++ b/tests/common/com/android/documentsui/ui/TestDialogController.java
@@ -28,6 +28,7 @@
     public int mNextConfirmationCode;
     private boolean mFileOpFailed;
     private boolean mNoApplicationFound;
+    private boolean mDocumentsClipped;
 
     public TestDialogController() {
         // by default, always confirm
@@ -51,6 +52,11 @@
         mNoApplicationFound = true;
     }
 
+    @Override
+    public void showDocumentsClipped(int size) {
+        mDocumentsClipped = true;
+    }
+
     public void assertNoFileFailures() {
         Assert.assertFalse(mFileOpFailed);
     }
@@ -59,6 +65,10 @@
         Assert.assertFalse(mNoApplicationFound);
     }
 
+    public void assertDocumentsClippedNotShown() {
+        Assert.assertFalse(mDocumentsClipped);
+    }
+
     public void confirmNext() {
         mNextConfirmationCode = ConfirmationCallback.CONFIRM;
     }
diff --git a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
index 5ac2741..b3379bf 100644
--- a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
@@ -59,6 +59,7 @@
                 mEnv.state,
                 mEnv.roots,
                 mEnv.docs,
+                mEnv.focusHandler,
                 mEnv.selectionMgr,
                 mEnv.searchViewManager,
                 mEnv::lookupExecutor) {
diff --git a/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java b/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java
index 9f9663f..9d9256d 100644
--- a/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/DocumentHolderTest.java
@@ -30,7 +30,6 @@
 import android.view.MotionEvent.PointerProperties;
 
 import com.android.documentsui.R;
-import com.android.documentsui.base.State;
 
 @SmallTest
 public class DocumentHolderTest extends AndroidTestCase {
@@ -44,7 +43,7 @@
         LayoutInflater inflater = LayoutInflater.from(context);
         mHolder = new DocumentHolder(getContext(), inflater.inflate(R.layout.item_doc_list, null)) {
             @Override
-            public void bind(Cursor cursor, String modelId, State state) {}
+            public void bind(Cursor cursor, String modelId) {}
         };
 
         mListener = new TestListener();
diff --git a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
index 271639b..c332bfd 100644
--- a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
@@ -36,6 +36,7 @@
 import com.android.documentsui.base.DocumentStack;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
+import com.android.documentsui.selection.Selection;
 import com.android.documentsui.testing.Roots;
 import com.android.documentsui.testing.TestConfirmationCallback;
 import com.android.documentsui.testing.TestEnv;
@@ -71,6 +72,7 @@
                 mEnv.state,
                 mEnv.roots,
                 mEnv.docs,
+                mEnv.focusHandler,
                 mEnv.selectionMgr,
                 mEnv.searchViewManager,
                 mEnv::lookupExecutor,
@@ -112,13 +114,32 @@
     }
 
     @Test
-    public void testDeleteSelectedDocuments() {
+    public void testCutSelectedDocuments_NoGivenSelection() {
         mEnv.populateStack();
 
+        mEnv.selectionMgr.clearSelection();
+        mHandler.cutToClipboard();
+        mDialogs.assertDocumentsClippedNotShown();
+    }
+
+    @Test
+    public void testCopySelectedDocuments_NoGivenSelection() {
+        mEnv.populateStack();
+
+        mEnv.selectionMgr.clearSelection();
+        mHandler.copyToClipboard();
+        mDialogs.assertDocumentsClippedNotShown();
+    }
+
+    @Test
+    public void testDeleteSelectedDocuments_NoSelection() {
+        mEnv.populateStack();
+
+        mEnv.selectionMgr.clearSelection();
         mHandler.deleteSelectedDocuments();
         mDialogs.assertNoFileFailures();
-        mActivity.startService.assertCalled();
-        mActionModeAddons.finishOnConfirmed.assertConfirmed();
+        mActivity.startService.assertNotCalled();
+        mActionModeAddons.finishOnConfirmed.assertNeverCalled();
     }
 
     @Test
diff --git a/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java b/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java
index 37a696f..b579dbe 100644
--- a/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java
+++ b/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.documentsui.files;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.support.test.filters.MediumTest;
@@ -24,50 +23,30 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
-import com.android.documentsui.dirlist.TestData;
-import com.android.documentsui.selection.SelectionManager;
-import com.android.documentsui.selection.SelectionProbe;
-import com.android.documentsui.testing.SelectionManagers;
-import com.android.documentsui.testing.TestActionHandler;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class ActivityInputHandlerTest {
 
-    private static final List<String> ITEMS = TestData.create(100);
-
-    private SelectionProbe mSelection;
-    private TestActionHandler mActionHandler;
     private ActivityInputHandler mActivityInputHandler;
+    private boolean mDeleteHappened;
 
     @Before
     public void setUp() {
-        SelectionManager selectionMgr = SelectionManagers.createTestInstance(ITEMS);
-        mSelection = new SelectionProbe(selectionMgr);
-        mActionHandler = new TestActionHandler();
-        mActivityInputHandler = new ActivityInputHandler(selectionMgr, mActionHandler);
+        mDeleteHappened = false;
+        mActivityInputHandler = new ActivityInputHandler(() -> {
+            mDeleteHappened = true;
+        });
     }
 
     @Test
-    public void testDelete_noSelection() {
-        KeyEvent event = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0,
-                KeyEvent.META_ALT_ON);
-        assertFalse(mActivityInputHandler.onKeyDown(event.getKeyCode(), event));
-        assertFalse(mActionHandler.mDeleteHappened);
-    }
-
-    @Test
-    public void testDelete_hasSelection() {
-        mSelection.select(1);
+    public void testDelete() {
         KeyEvent event = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0,
                 KeyEvent.META_ALT_ON);
         assertTrue(mActivityInputHandler.onKeyDown(event.getKeyCode(), event));
-        assertTrue(mActionHandler.mDeleteHappened);
+        assertTrue(mDeleteHappened);
     }
 }
diff --git a/tests/unit/com/android/documentsui/files/MenuManagerTest.java b/tests/unit/com/android/documentsui/files/MenuManagerTest.java
index 5d1fb7d..bad3e66 100644
--- a/tests/unit/com/android/documentsui/files/MenuManagerTest.java
+++ b/tests/unit/com/android/documentsui/files/MenuManagerTest.java
@@ -173,7 +173,7 @@
     @Test
     public void testOptionMenu_showAdvanced() {
         state.showAdvanced = true;
-        state.showAdvancedOption = true;
+        state.showDeviceStorageOption = true;
         mgr.updateOptionMenu(testMenu);
 
         advanced.assertVisible();
diff --git a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
index 73cb123..5e26cd8 100644
--- a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
@@ -65,6 +65,7 @@
                 mEnv.state,
                 mEnv.roots,
                 mEnv.docs,
+                mEnv.focusHandler,
                 mEnv.selectionMgr,
                 mEnv.searchViewManager,
                 mEnv::lookupExecutor,
diff --git a/tests/unit/com/android/documentsui/picker/MenuManagerTest.java b/tests/unit/com/android/documentsui/picker/MenuManagerTest.java
index f282bd7..0e6a53a 100644
--- a/tests/unit/com/android/documentsui/picker/MenuManagerTest.java
+++ b/tests/unit/com/android/documentsui/picker/MenuManagerTest.java
@@ -164,7 +164,7 @@
     @Test
     public void testOptionMenu_showAdvanced() {
         state.showAdvanced = true;
-        state.showAdvancedOption = true;
+        state.showDeviceStorageOption = true;
         mgr.updateOptionMenu(testMenu);
 
         advanced.assertVisible();
diff --git a/tests/unit/com/android/documentsui/queries/DebugCommandProcessorTest.java b/tests/unit/com/android/documentsui/queries/DebugCommandProcessorTest.java
index 0f6e996..2398bb2 100644
--- a/tests/unit/com/android/documentsui/queries/DebugCommandProcessorTest.java
+++ b/tests/unit/com/android/documentsui/queries/DebugCommandProcessorTest.java
@@ -16,11 +16,14 @@
 
 package com.android.documentsui.queries;
 
+import static com.android.documentsui.queries.DebugCommandProcessor.COMMAND_PREFIX;
+
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.documentsui.testing.TestEventHandler;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,7 +45,7 @@
 
     @Test
     public void testTriesAllCommands() {
-        mProcessor.accept("#debug poodles");
+        mProcessor.accept(COMMAND_PREFIX + "poodles");
         mCommand0.assertCalled();
         mCommand1.assertCalled();
     }
@@ -50,21 +53,23 @@
     @Test
     public void testStopsAfterCommandHandled() {
         mCommand0.nextReturn(true);
-        mProcessor.accept("#debug poodles");
+        mProcessor.accept("dbg:poodles");
         mCommand0.assertCalled();
         mCommand1.assertNotCalled();
     }
 
     @Test
-    public void testMissingCommand() {
-        mProcessor.accept("#debug");
-        mCommand0.assertNotCalled();
-        mCommand1.assertNotCalled();
+    public void testConveysArguments() {
+        mCommand0.nextReturn(true);
+        mProcessor.accept(COMMAND_PREFIX + "cheese doodles");
+
+        String[] expected = {"cheese", "doodles"};
+        Assert.assertArrayEquals(expected, mCommand0.getLastValue());
     }
 
     @Test
-    public void testEmptyInput() {
-        mProcessor.accept("#debug");
+    public void testMissingCommand() {
+        mProcessor.accept(COMMAND_PREFIX);
         mCommand0.assertNotCalled();
         mCommand1.assertNotCalled();
     }