diff options
16 files changed, 136 insertions, 39 deletions
diff --git a/res/drawable/drag_shadow_background.xml b/res/drawable/drag_shadow_background.xml index 49465cbab..d6ec373d5 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 000000000..b92c30a66 --- /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 9da77420e..1353d6fe3 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 235757e43..a88c4e2cd 100644 --- a/src/com/android/documentsui/BaseActivity.java +++ b/src/com/android/documentsui/BaseActivity.java @@ -479,6 +479,11 @@ public abstract class BaseActivity<T extends ActionHandler> 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 index 0092f6cc6..d6877ec00 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.view.View; 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 @@ final class DragShadowBuilder extends View.DragShadowBuilder { 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 @@ final class DragShadowBuilder extends View.DragShadowBuilder { @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 d5b8f4ff6..f8062a76b 100644 --- a/src/com/android/documentsui/DrawerController.java +++ b/src/com/android/documentsui/DrawerController.java @@ -160,7 +160,7 @@ public abstract class DrawerController implements DrawerListener { } @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 e804e47cf..881fbbccf 100644 --- a/src/com/android/documentsui/HorizontalBreadcrumb.java +++ b/src/com/android/documentsui/HorizontalBreadcrumb.java @@ -126,7 +126,7 @@ public final class HorizontalBreadcrumb extends RecyclerView } @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 bb209094a..79f71d964 100644 --- a/src/com/android/documentsui/ItemDragListener.java +++ b/src/com/android/documentsui/ItemDragListener.java @@ -78,7 +78,7 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { } 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 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { /** * 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 57175a9aa..044d688a0 100644 --- a/src/com/android/documentsui/dirlist/DirectoryDragListener.java +++ b/src/com/android/documentsui/dirlist/DirectoryDragListener.java @@ -35,9 +35,17 @@ class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { 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 @@ class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { @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 2885a4b0a..84d8b0565 100644 --- a/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -335,7 +335,8 @@ public class DirectoryFragment extends Fragment 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 @@ public class DirectoryFragment extends Fragment } } + 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 @@ public class DirectoryFragment extends Fragment * 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 @@ public class DirectoryFragment extends Fragment assert(DocumentClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); - if (!shouldCopyTo(event.getLocalState(), v)) { + if (!canCopyTo(event.getLocalState(), v)) { return false; } @@ -946,7 +960,7 @@ public class DirectoryFragment extends Fragment // 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 5bf41d70c..af864d942 100644 --- a/src/com/android/documentsui/dirlist/DragStartListener.java +++ b/src/com/android/documentsui/dirlist/DragStartListener.java @@ -25,13 +25,14 @@ import android.support.annotation.VisibleForTesting; 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 @@ interface DragStartListener { State state, Function<View, String> idFinder, ViewFinder viewFinder, - Drawable defaultDragIcon) { - - DragShadowBuilder.Factory shadowFactory = - new DragShadowBuilder.Factory(context, model, iconHelper, defaultDragIcon); + Drawable defaultDragIcon, + DragShadowBuilder shadowBuilder) { + + 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 c9fdc1f62..05dbcb289 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.ActionModeController; 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 @@ public class FilesActivity private DocumentClipper mClipper; private ActionModeController mActionModeController; private ActivityInputHandler mActivityInputHandler; + private DragShadowBuilder mShadowBuilder; public FilesActivity() { super(R.layout.files_activity, TAG); @@ -99,9 +101,9 @@ public class FilesActivity return mClipper.hasItemsToPaste(); } }); - mDialogs = DialogController.create(this, getMessages()); + mShadowBuilder = new DragShadowBuilder(this); mActionModeController = new ActionModeController( this, mSelectionMgr, @@ -296,6 +298,11 @@ public class FilesActivity } @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 ecdc0536e..8890fb3a8 100644 --- a/src/com/android/documentsui/sidebar/RootsFragment.java +++ b/src/com/android/documentsui/sidebar/RootsFragment.java @@ -376,7 +376,7 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost * 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 6293c459d..865d8b6c6 100644 --- a/tests/unit/com/android/documentsui/ItemDragListenerTest.java +++ b/tests/unit/com/android/documentsui/ItemDragListenerTest.java @@ -217,7 +217,7 @@ public class ItemDragListenerTest { } @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 daae554e8..fc5933a3e 100644 --- a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java +++ b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java @@ -200,7 +200,7 @@ public class DragScrollListenerTest { } @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 f2e044b04..b89865390 100644 --- a/tests/unit/com/android/documentsui/dirlist/DragStartListenerTest.java +++ b/tests/unit/com/android/documentsui/dirlist/DragStartListenerTest.java @@ -24,6 +24,7 @@ import android.view.View; 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; |