Merge "Updating DropShadow as drag and drop hovers over views." into nyc-andromeda-dev
diff --git a/res/drawable/drag_shadow_background.xml b/res/drawable/drag_shadow_background.xml
index 49465cb..d6ec373 100644
--- a/res/drawable/drag_shadow_background.xml
+++ b/res/drawable/drag_shadow_background.xml
@@ -16,7 +16,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-  <solid android:color="@color/item_doc_background" />
+  <solid android:color="@color/item_drag_shadow_background" />
   <stroke
       android:width="1dp"
       android:color="#ff9f9f9f" />
diff --git a/res/drawable/drag_shadow_background_no_drop.xml b/res/drawable/drag_shadow_background_no_drop.xml
new file mode 100644
index 0000000..b92c30a
--- /dev/null
+++ b/res/drawable/drag_shadow_background_no_drop.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+  <solid android:color="@color/item_drag_shadow_background_no_drop" />
+  <stroke
+      android:width="1dp"
+      android:color="#ff9f9f9f" />
+  <corners
+      android:bottomRightRadius="3dp"
+      android:bottomLeftRadius="3dp"
+      android:topLeftRadius="3dp"
+      android:topRightRadius="3dp"/>
+</shape>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 9da7742..1353d6f 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -39,4 +39,7 @@
     <color name="item_doc_background_selected">@*android:color/accent_device_default_50</color>
     <color name="item_breadcrumb_background_hovered">#1affffff</color>
 
+    <color name="item_drag_shadow_background">#fffafafa</color>
+    <color name="item_drag_shadow_background_no_drop">#ffffb6c1</color>
+
 </resources>
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index d0160ce..3928a01 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -485,6 +485,11 @@
         return mState;
     }
 
+    public DragShadowBuilder getShadowBuilder() {
+        throw new UnsupportedOperationException(
+                "Drag and drop not supported, can't get shadow builder");
+    }
+
     /**
      * Set internal storage visible based on explicit user action.
      */
diff --git a/src/com/android/documentsui/dirlist/DragShadowBuilder.java b/src/com/android/documentsui/DragShadowBuilder.java
similarity index 72%
rename from src/com/android/documentsui/dirlist/DragShadowBuilder.java
rename to src/com/android/documentsui/DragShadowBuilder.java
index 0092f6c..d6877ec 100644
--- a/src/com/android/documentsui/dirlist/DragShadowBuilder.java
+++ b/src/com/android/documentsui/DragShadowBuilder.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.documentsui.dirlist;
+package com.android.documentsui;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -26,32 +26,37 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import com.android.documentsui.R;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.Shared;
+import com.android.documentsui.dirlist.IconHelper;
+import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.selection.Selection;
 
 import java.util.List;
 import java.util.function.Function;
 
-final class DragShadowBuilder extends View.DragShadowBuilder {
+public final class DragShadowBuilder extends View.DragShadowBuilder {
 
     private final View mShadowView;
     private final TextView mTitle;
     private final ImageView mIcon;
     private final int mWidth;
     private final int mHeight;
+    private final Drawable mDefaultBackground;
+    private final Drawable mNoDropBackground;
 
-    public DragShadowBuilder(Context context, String title, Drawable icon) {
+    public DragShadowBuilder(Context context) {
         mWidth = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_width);
-        mHeight= context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
+        mHeight = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
 
         mShadowView = LayoutInflater.from(context).inflate(R.layout.drag_shadow_layout, null);
         mTitle = (TextView) mShadowView.findViewById(android.R.id.title);
         mIcon = (ImageView) mShadowView.findViewById(android.R.id.icon);
 
-        mTitle.setText(title);
-        mIcon.setImageDrawable(icon);
+        mDefaultBackground = context.getResources().getDrawable(R.drawable.drag_shadow_background,
+                null);
+        mNoDropBackground = context.getResources()
+                .getDrawable(R.drawable.drag_shadow_background_no_drop, null);
     }
 
     @Override
@@ -72,20 +77,39 @@
         mShadowView.draw(canvas);
     }
 
+    public void updateTitle(String title) {
+        mTitle.setText(title);
+    }
+
+    public void updateIcon(Drawable icon) {
+        mIcon.setImageDrawable(icon);
+    }
+
+    public void resetBackground() {
+        mShadowView.setBackground(mDefaultBackground);
+    }
+
+    public void setNoDropBackground() {
+        mShadowView.setBackground(mNoDropBackground);
+    }
+
     /**
      * Provides a means of fully isolating the mechanics of building drag shadows (and builders)
      * in support of testing.
      */
-    public static final class Factory implements Function<Selection, DragShadowBuilder> {
+    public static final class Updater implements Function<Selection, DragShadowBuilder> {
 
         private final Context mContext;
         private final IconHelper mIconHelper;
         private final Drawable mDefaultDragIcon;
-        private Model mModel;
+        private final Model mModel;
+        private final DragShadowBuilder mShadowBuilder;
 
-        public Factory(
-                Context context, Model model, IconHelper iconHelper, Drawable defaultDragIcon) {
+        public Updater(
+                Context context, DragShadowBuilder shadowBuilder, Model model,
+                IconHelper iconHelper, Drawable defaultDragIcon) {
             mContext = context;
+            mShadowBuilder = shadowBuilder;
             mModel = model;
             mIconHelper = iconHelper;
             mDefaultDragIcon = defaultDragIcon;
@@ -93,10 +117,10 @@
 
         @Override
         public DragShadowBuilder apply(Selection selection) {
-            return new DragShadowBuilder(
-                    mContext,
-                    getDragTitle(selection),
-                    getDragIcon(selection));
+            mShadowBuilder.updateTitle(getDragTitle(selection));
+            mShadowBuilder.updateIcon(getDragIcon(selection));
+
+            return mShadowBuilder;
         }
 
         private Drawable getDragIcon(Selection selection) {
diff --git a/src/com/android/documentsui/DrawerController.java b/src/com/android/documentsui/DrawerController.java
index d5b8f4f..f8062a7 100644
--- a/src/com/android/documentsui/DrawerController.java
+++ b/src/com/android/documentsui/DrawerController.java
@@ -160,7 +160,7 @@
         }
 
         @Override
-        public void onDragEntered(View v) {
+        public void onDragEntered(View v, Object localState) {
             assert (v.getId() == R.id.drawer_edge);
 
             setOpen(true);
diff --git a/src/com/android/documentsui/HorizontalBreadcrumb.java b/src/com/android/documentsui/HorizontalBreadcrumb.java
index e804e47..881fbbc 100644
--- a/src/com/android/documentsui/HorizontalBreadcrumb.java
+++ b/src/com/android/documentsui/HorizontalBreadcrumb.java
@@ -126,7 +126,7 @@
     }
 
     @Override
-    public void onDragEntered(View v) {
+    public void onDragEntered(View v, Object localState) {
         // do nothing
     }
 
diff --git a/src/com/android/documentsui/ItemDragListener.java b/src/com/android/documentsui/ItemDragListener.java
index bb20909..79f71d9 100644
--- a/src/com/android/documentsui/ItemDragListener.java
+++ b/src/com/android/documentsui/ItemDragListener.java
@@ -78,7 +78,7 @@
     }
 
     private void handleEnteredEvent(View v, DragEvent event) {
-        mDragHost.onDragEntered(v);
+        mDragHost.onDragEntered(v, event.getLocalState());
         @Nullable TimerTask task = createOpenTask(v, event);
         if (task == null) {
             return;
@@ -167,7 +167,8 @@
         /**
          * Notifies right away when drag shadow enters the view
          * @param v the view which drop shadow just entered
+         * @param localState the Local state object given by DragEvent
          */
-        void onDragEntered(View v);
+        void onDragEntered(View v, Object localState);
     }
 }
diff --git a/src/com/android/documentsui/dirlist/DirectoryDragListener.java b/src/com/android/documentsui/dirlist/DirectoryDragListener.java
index 57175a9..044d688 100644
--- a/src/com/android/documentsui/dirlist/DirectoryDragListener.java
+++ b/src/com/android/documentsui/dirlist/DirectoryDragListener.java
@@ -35,9 +35,17 @@
     public boolean onDrag(View v, DragEvent event) {
         final boolean result = super.onDrag(v, event);
 
-        if (event.getAction() == DragEvent.ACTION_DRAG_ENDED) {
-            // getResult() is true if drag was accepted
-            mDragHost.dragStopped(event.getResult());
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_EXITED:
+                // If drag exits, we want to update drag and drop status on the drop shadow
+                mDragHost.dragExited(v);
+                break;
+            case DragEvent.ACTION_DRAG_ENDED:
+                // getResult() is true if drag was accepted
+                mDragHost.dragStopped(event.getResult());
+                break;
+            default:
+                break;
         }
 
         return result;
@@ -50,7 +58,7 @@
 
     @Override
     public @Nullable TimerTask createOpenTask(final View v, DragEvent event) {
-        return mDragHost.shouldCopyTo(event.getLocalState(), v) ?
+        return mDragHost.canCopyTo(event.getLocalState(), v) ?
                 super.createOpenTask(v, event) : null;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2885a4b..84d8b05 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -335,7 +335,8 @@
                         mState,
                         this::getModelId,
                         mRecView::findChildViewUnder,
-                        getContext().getDrawable(com.android.internal.R.drawable.ic_doc_generic))
+                        getContext().getDrawable(com.android.internal.R.drawable.ic_doc_generic),
+                        mActivity.getShadowBuilder())
                 : DragStartListener.DUMMY;
 
         EventHandler<InputEvent> gestureHandler = mState.allowMultiple
@@ -880,6 +881,12 @@
         }
     }
 
+    void dragExited(View v) {
+        // For now, just always reset drag shadow when drag exits
+        mActivity.getShadowBuilder().resetBackground();
+        v.updateDragShadow(mActivity.getShadowBuilder());
+    }
+
     void dragStopped(boolean result) {
         if (result) {
             mSelectionMgr.clearSelection();
@@ -897,8 +904,15 @@
      * In DirectoryFragment, we close the roots drawer right away.
      */
     @Override
-    public void onDragEntered(View view) {
-        mActivity.setRootsDrawerOpen(false);
+    public void onDragEntered(View v, Object localState) {
+    mActivity.setRootsDrawerOpen(false);
+
+        if (canCopyTo(localState, v)) {
+            mActivity.getShadowBuilder().resetBackground();
+        } else {
+            mActivity.getShadowBuilder().setNoDropBackground();
+        }
+        v.updateDragShadow(mActivity.getShadowBuilder());
     }
 
     /**
@@ -924,7 +938,7 @@
 
         assert(DocumentClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY);
 
-        if (!shouldCopyTo(event.getLocalState(), v)) {
+        if (!canCopyTo(event.getLocalState(), v)) {
             return false;
         }
 
@@ -946,7 +960,7 @@
     // Don't copy from the cwd into a provided list of prohibited directories. (ie. into cwd, into
     // a selected directory). Note: this currently doesn't work for multi-window drag, because
     // localState isn't carried over from one process to another.
-    boolean shouldCopyTo(Object dragLocalState, View destinationView) {
+    boolean canCopyTo(Object dragLocalState, View destinationView) {
         if (dragLocalState == null || !(dragLocalState instanceof List<?>)) {
             if (DEBUG) Log.d(TAG, "Invalid local state object. Will allow copy.");
             return true;
diff --git a/src/com/android/documentsui/dirlist/DragStartListener.java b/src/com/android/documentsui/dirlist/DragStartListener.java
index 5bf41d7..af864d9 100644
--- a/src/com/android/documentsui/dirlist/DragStartListener.java
+++ b/src/com/android/documentsui/dirlist/DragStartListener.java
@@ -25,13 +25,14 @@
 import android.util.Log;
 import android.view.View;
 
+import com.android.documentsui.DragShadowBuilder;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.Events;
-import com.android.documentsui.base.State;
 import com.android.documentsui.base.Events.InputEvent;
+import com.android.documentsui.base.State;
 import com.android.documentsui.clipping.DocumentClipper;
-import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.selection.Selection;
+import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.services.FileOperationService;
 import com.android.documentsui.services.FileOperationService.OpType;
 
@@ -190,10 +191,15 @@
             State state,
             Function<View, String> idFinder,
             ViewFinder viewFinder,
-            Drawable defaultDragIcon) {
+            Drawable defaultDragIcon,
+            DragShadowBuilder shadowBuilder) {
 
-        DragShadowBuilder.Factory shadowFactory =
-                new DragShadowBuilder.Factory(context, model, iconHelper, defaultDragIcon);
+        DragShadowBuilder.Updater shadowFactory = new DragShadowBuilder.Updater(
+                context,
+                shadowBuilder,
+                model,
+                iconHelper,
+                defaultDragIcon);
 
         return new ActiveListener(
                 state,
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 5777f9c..877914b 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -37,6 +37,7 @@
 import com.android.documentsui.ActivityConfig;
 import com.android.documentsui.BaseActivity;
 import com.android.documentsui.DocumentsApplication;
+import com.android.documentsui.DragShadowBuilder;
 import com.android.documentsui.MenuManager.DirectoryDetails;
 import com.android.documentsui.MenuManager.SelectionDetails;
 import com.android.documentsui.OperationDialogFragment;
@@ -79,6 +80,7 @@
     private DocumentClipper mClipper;
     private ActionModeController mActionModeController;
     private ActivityInputHandler mActivityInputHandler;
+    private DragShadowBuilder mShadowBuilder;
 
     public FilesActivity() {
         super(R.layout.files_activity, TAG);
@@ -99,9 +101,9 @@
                         return mClipper.hasItemsToPaste();
                     }
                 });
-
         mDialogs = DialogController.create(this, getMessages());
 
+        mShadowBuilder = new DragShadowBuilder(this);
         mActionModeController = new ActionModeController(
                 this,
                 mSelectionMgr,
@@ -297,6 +299,11 @@
     }
 
     @Override
+    public DragShadowBuilder getShadowBuilder() {
+        return mShadowBuilder;
+    }
+
+    @Override
     public boolean onKeyShortcut(int keyCode, KeyEvent event) {
         DirectoryFragment dir;
         // TODO: All key events should be statically bound using alphabeticShortcut.
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index ecdc053..8890fb3 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -376,7 +376,7 @@
      * In RootsFragment we don't do anything
      */
     @Override
-    public void onDragEntered(View v) {
+    public void onDragEntered(View v, Object localState) {
     }
 
     /**
diff --git a/tests/unit/com/android/documentsui/ItemDragListenerTest.java b/tests/unit/com/android/documentsui/ItemDragListenerTest.java
index 6293c45..865d8b6 100644
--- a/tests/unit/com/android/documentsui/ItemDragListenerTest.java
+++ b/tests/unit/com/android/documentsui/ItemDragListenerTest.java
@@ -217,7 +217,7 @@
         }
 
         @Override
-        public void onDragEntered(View v) {
+        public void onDragEntered(View v, Object localState) {
             mLastEnteredView = v;
         }
     }
diff --git a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
index daae554..fc5933a 100644
--- a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
@@ -200,7 +200,7 @@
         }
 
         @Override
-        public void onDragEntered(View v) {
+        public void onDragEntered(View v, Object localState) {
         }
     }
 
diff --git a/tests/unit/com/android/documentsui/dirlist/DragStartListenerTest.java b/tests/unit/com/android/documentsui/dirlist/DragStartListenerTest.java
index f2e044b..b898653 100644
--- a/tests/unit/com/android/documentsui/dirlist/DragStartListenerTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/DragStartListenerTest.java
@@ -24,6 +24,7 @@
 
 import com.android.documentsui.base.State;
 import com.android.documentsui.dirlist.DragStartListener.ActiveListener;
+import com.android.documentsui.DragShadowBuilder;
 import com.android.documentsui.base.Events.InputEvent;
 import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.selection.Selection;