Merge "Make Auto-scrolling base purely on edge offset, no longer on time." into nyc-andromeda-dev
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index d9d6cf4..9f5e66c 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -47,6 +47,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
diff --git a/src/com/android/documentsui/DrawerController.java b/src/com/android/documentsui/DrawerController.java
index ebac991..2b181f2 100644
--- a/src/com/android/documentsui/DrawerController.java
+++ b/src/com/android/documentsui/DrawerController.java
@@ -153,18 +153,17 @@
 
         @Override
         public void setDropTargetHighlight(View v, boolean highlight) {
+        }
+
+        @Override
+        public void onDragEntered(View v) {
             assert (v.getId() == R.id.drawer_edge);
 
-            @ColorRes int id = highlight ? R.color.item_doc_background_selected :
-                android.R.color.transparent;
-            v.setBackgroundColor(id);
+            setOpen(true);
         }
 
         @Override
         public void onViewHovered(View v) {
-            assert (v.getId() == R.id.drawer_edge);
-
-            setOpen(true);
         }
 
         @Override
diff --git a/src/com/android/documentsui/HorizontalBreadcrumb.java b/src/com/android/documentsui/HorizontalBreadcrumb.java
index 6ed1b5c..e804e47 100644
--- a/src/com/android/documentsui/HorizontalBreadcrumb.java
+++ b/src/com/android/documentsui/HorizontalBreadcrumb.java
@@ -126,6 +126,11 @@
     }
 
     @Override
+    public void onDragEntered(View v) {
+        // do nothing
+    }
+
+    @Override
     public void onViewHovered(View v) {
         int pos = getChildAdapterPosition(v);
         if (pos != mAdapter.getItemCount() - 1) {
diff --git a/src/com/android/documentsui/ItemDragListener.java b/src/com/android/documentsui/ItemDragListener.java
index 2d67ff9..bb20909 100644
--- a/src/com/android/documentsui/ItemDragListener.java
+++ b/src/com/android/documentsui/ItemDragListener.java
@@ -40,7 +40,7 @@
     private static final String TAG = "ItemDragListener";
 
     @VisibleForTesting
-    static final int SPRING_TIMEOUT = 1000;
+    static final int SPRING_TIMEOUT = 1500;
 
     protected final H mDragHost;
     private final Timer mHoverTimer;
@@ -78,6 +78,7 @@
     }
 
     private void handleEnteredEvent(View v, DragEvent event) {
+        mDragHost.onDragEntered(v);
         @Nullable TimerTask task = createOpenTask(v, event);
         if (task == null) {
             return;
@@ -162,5 +163,11 @@
          * @param v the view being hovered
          */
         void onViewHovered(View v);
+
+        /**
+         * Notifies right away when drag shadow enters the view
+         * @param v the view which drop shadow just entered
+         */
+        void onDragEntered(View v);
     }
 }
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java
index c6e9514..12c6217 100644
--- a/src/com/android/documentsui/MenuManager.java
+++ b/src/com/android/documentsui/MenuManager.java
@@ -17,6 +17,7 @@
 package com.android.documentsui;
 
 import android.app.Fragment;
+import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -27,10 +28,14 @@
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
+import com.android.documentsui.dirlist.ActionModeController;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.sidebar.RootsFragment;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.List;
+import java.util.function.IntFunction;
+
 public abstract class MenuManager {
 
     final protected SearchViewManager mSearchManager;
diff --git a/src/com/android/documentsui/base/Events.java b/src/com/android/documentsui/base/Events.java
index 2231ab5..4ec7dea 100644
--- a/src/com/android/documentsui/base/Events.java
+++ b/src/com/android/documentsui/base/Events.java
@@ -132,6 +132,9 @@
          * or touch. */
         boolean isActionMove();
 
+        /** Returns true if the action is cancel. */
+        boolean isActionCancel();
+
         // Eliminate the checked Exception from Autoclosable.
         @Override
         public void close();
@@ -254,6 +257,11 @@
         }
 
         @Override
+        public boolean isActionCancel() {
+            return mEvent.getActionMasked() == MotionEvent.ACTION_CANCEL;
+        }
+
+        @Override
         public Point getOrigin() {
             return new Point((int) mEvent.getX(), (int) mEvent.getY());
         }
@@ -326,8 +334,8 @@
                     .append(" isPrimaryButtonPressed=").append(isPrimaryButtonPressed())
                     .append(" isSecondaryButtonPressed=").append(isSecondaryButtonPressed())
                     .append(" isShiftKeyDown=").append(isShiftKeyDown())
-                    .append(" isActionDown=").append(isActionDown())
-                    .append(" isActionUp=").append(isActionUp())
+                    .append(" action(decoded)=").append(
+                            MotionEvent.actionToString(mEvent.getActionMasked()))
                     .append(" getOrigin=").append(getOrigin())
                     .append(" isOverItem=").append(isOverItem())
                     .append(" getItemPosition=").append(getItemPosition())
diff --git a/src/com/android/documentsui/dirlist/BandController.java b/src/com/android/documentsui/dirlist/BandController.java
index 16b0a03..c938d93 100644
--- a/src/com/android/documentsui/dirlist/BandController.java
+++ b/src/com/android/documentsui/dirlist/BandController.java
@@ -228,7 +228,7 @@
     public boolean shouldStop(InputEvent input) {
         return isActive()
                 && input.isMouseEvent()
-                && input.isActionUp();
+                && (input.isActionUp() || input.isActionCancel());
     }
 
     /**
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 73c1c15..32ea333 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -1026,11 +1026,21 @@
     /**
      * {@inheritDoc}
      *
+     * In DirectoryFragment, we close the roots drawer right away.
+     */
+    @Override
+    public void onDragEntered(View view) {
+        getBaseActivity().setRootsDrawerOpen(false);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
      * In DirectoryFragment, we spring loads the hovered folder.
      */
     @Override
     public void onViewHovered(View view) {
-        BaseActivity activity = (BaseActivity) getActivity();
+        BaseActivity activity = getBaseActivity();
         if (getModelId(view) != null) {
            activity.springOpenDirectory(getDestination(view));
         }
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 116472a..2d1ed94 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -31,6 +31,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuItem;
 
@@ -225,6 +226,12 @@
     }
 
     @Override
+    public void onProvideKeyboardShortcuts(
+            List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
+        getMenuManager().updateKeyboardShortcutsMenu(data, this::getString);
+    }
+
+    @Override
     public void refreshDirectory(@AnimationType int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
diff --git a/src/com/android/documentsui/files/MenuManager.java b/src/com/android/documentsui/files/MenuManager.java
index f2286f4..61d57b5 100644
--- a/src/com/android/documentsui/files/MenuManager.java
+++ b/src/com/android/documentsui/files/MenuManager.java
@@ -17,17 +17,24 @@
 package com.android.documentsui.files;
 
 import android.app.Fragment;
+import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 
+import com.android.documentsui.BaseActivity;
 import com.android.documentsui.R;
 import com.android.documentsui.SearchViewManager;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.State;
 
+import java.util.List;
+import java.util.function.IntFunction;
+
 public final class MenuManager extends com.android.documentsui.MenuManager {
 
     public MenuManager(
@@ -45,6 +52,34 @@
         mSearchManager.updateMenu();
     }
 
+    /**
+     * @see FilesActivity#onProvideKeyboardShortcuts(List, Menu, int)
+     */
+    public void updateKeyboardShortcutsMenu(
+            List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier) {
+        KeyboardShortcutGroup group = new KeyboardShortcutGroup(
+                stringSupplier.apply(R.string.app_label));
+        group.addItem(new KeyboardShortcutInfo(
+                stringSupplier.apply(R.string.menu_cut_to_clipboard), KeyEvent.KEYCODE_X,
+                KeyEvent.META_CTRL_ON));
+        group.addItem(new KeyboardShortcutInfo(
+                stringSupplier.apply(R.string.menu_copy_to_clipboard), KeyEvent.KEYCODE_C,
+                KeyEvent.META_CTRL_ON));
+        group.addItem(new KeyboardShortcutInfo(
+                stringSupplier.apply(R.string.menu_paste_from_clipboard), KeyEvent.KEYCODE_V,
+                KeyEvent.META_CTRL_ON));
+        group.addItem(new KeyboardShortcutInfo(
+                stringSupplier.apply(R.string.menu_create_dir), KeyEvent.KEYCODE_E,
+                KeyEvent.META_CTRL_ON));
+        group.addItem(new KeyboardShortcutInfo(
+                stringSupplier.apply(R.string.menu_select_all), KeyEvent.KEYCODE_A,
+                KeyEvent.META_CTRL_ON));
+        group.addItem(new KeyboardShortcutInfo(
+                stringSupplier.apply(R.string.menu_new_window), KeyEvent.KEYCODE_N,
+                KeyEvent.META_CTRL_ON));
+        data.add(group);
+    }
+
     @Override
     public void showContextMenu(Fragment f, View v, float x, float y) {
         // Register context menu here so long-press doesn't trigger this context floating menu.
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index c39be7d..1174503 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -362,6 +362,15 @@
     /**
      * {@inheritDoc}
      *
+     * In RootsFragment we don't do anything
+     */
+    @Override
+    public void onDragEntered(View v) {
+    }
+
+    /**
+     * {@inheritDoc}
+     *
      * In RootsFragment we open the hovered root.
      */
     @Override
diff --git a/tests/common/com/android/documentsui/testing/TestEvent.java b/tests/common/com/android/documentsui/testing/TestEvent.java
index 9fd6215..c67f514 100644
--- a/tests/common/com/android/documentsui/testing/TestEvent.java
+++ b/tests/common/com/android/documentsui/testing/TestEvent.java
@@ -166,6 +166,11 @@
     }
 
     @Override
+    public boolean isActionCancel() {
+        return mAction == MotionEvent.ACTION_CANCEL;
+    }
+
+    @Override
     public boolean isOverItem() {
         return mDetails.isOverItem();
     }
diff --git a/tests/unit/com/android/documentsui/ItemDragListenerTest.java b/tests/unit/com/android/documentsui/ItemDragListenerTest.java
index e1fb87a..6293c45 100644
--- a/tests/unit/com/android/documentsui/ItemDragListenerTest.java
+++ b/tests/unit/com/android/documentsui/ItemDragListenerTest.java
@@ -116,7 +116,8 @@
 
         mTestTimer.fastForwardTo(DELAY_AFTER_HOVERING);
 
-        assertSame(mTestView, mTestDragHost.mLastOpenedView);
+        assertSame(mTestView, mTestDragHost.mLastEnteredView);
+        assertSame(mTestView, mTestDragHost.mLastHoveredView);
     }
 
     @Test
@@ -127,7 +128,8 @@
 
         mTestTimer.fastForwardTo(DELAY_AFTER_HOVERING);
 
-        assertNull(mTestDragHost.mLastOpenedView);
+        assertSame(mTestView, mTestDragHost.mLastEnteredView);
+        assertNull(mTestDragHost.mLastHoveredView);
     }
 
     @Test
@@ -138,7 +140,8 @@
 
         mTestTimer.fastForwardTo(DELAY_AFTER_HOVERING);
 
-        assertNull(mTestDragHost.mLastOpenedView);
+        assertSame(mTestView, mTestDragHost.mLastEnteredView);
+        assertNull(mTestDragHost.mLastHoveredView);
     }
 
     @Test
@@ -195,7 +198,8 @@
 
     private static class TestDragHost implements ItemDragListener.DragHost {
         private View mHighlightedView;
-        private View mLastOpenedView;
+        private View mLastHoveredView;
+        private View mLastEnteredView;
 
         @Override
         public void setDropTargetHighlight(View v, boolean highlight) {
@@ -209,7 +213,12 @@
 
         @Override
         public void onViewHovered(View v) {
-            mLastOpenedView = v;
+            mLastHoveredView = v;
+        }
+
+        @Override
+        public void onDragEntered(View v) {
+            mLastEnteredView = v;
         }
     }
 }
diff --git a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
index abae805..46d9c67 100644
--- a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
@@ -206,6 +206,10 @@
         @Override
         public void onViewHovered(View v) {
         }
+
+        @Override
+        public void onDragEntered(View v) {
+        }
     }
 
     private class TestScrollActionDelegate implements ScrollActionDelegate {
diff --git a/tests/unit/com/android/documentsui/files/MenuManagerTest.java b/tests/unit/com/android/documentsui/files/MenuManagerTest.java
index a7b4fe8..fd1f6cf 100644
--- a/tests/unit/com/android/documentsui/files/MenuManagerTest.java
+++ b/tests/unit/com/android/documentsui/files/MenuManagerTest.java
@@ -28,7 +28,6 @@
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.State;
-import com.android.documentsui.files.MenuManager;
 import com.android.documentsui.testing.TestDirectoryDetails;
 import com.android.documentsui.testing.TestMenu;
 import com.android.documentsui.testing.TestMenuInflater;