ItemDragListener.java revision 7f72a3c30ea89e2655ea6c453f8964bfedf4474b
1804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan/* 2804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Copyright (C) 2016 The Android Open Source Project 3804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * 4804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Licensed under the Apache License, Version 2.0 (the "License"); 5804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * you may not use this file except in compliance with the License. 6804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * You may obtain a copy of the License at 7804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * 8804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * http://www.apache.org/licenses/LICENSE-2.0 9804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * 10804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Unless required by applicable law or agreed to in writing, software 11804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * distributed under the License is distributed on an "AS IS" BASIS, 12804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * See the License for the specific language governing permissions and 14804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * limitations under the License. 15804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 16804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 17804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanpackage com.android.documentsui; 18804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 19804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.content.ClipData; 2057facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tanimport android.graphics.drawable.Drawable; 21804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.util.Log; 22804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.DragEvent; 23804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.View; 24804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.View.OnDragListener; 25804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 26804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport com.android.documentsui.ItemDragListener.DragHost; 27804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport com.android.internal.annotations.VisibleForTesting; 28804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 29804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport java.util.Timer; 30804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport java.util.TimerTask; 31804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 325a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Linimport javax.annotation.Nullable; 335a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin 34804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan/** 35804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want 36804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * items to pop-open when user hovers on them during a drag n drop. 37804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 38804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanpublic class ItemDragListener<H extends DragHost> implements OnDragListener { 39804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 40804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan private static final String TAG = "ItemDragListener"; 41804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 42804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @VisibleForTesting 437f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin static final int SPRING_TIMEOUT = 1500; 44804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 45804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan protected final H mDragHost; 46804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan private final Timer mHoverTimer; 47804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 48804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public ItemDragListener(H dragHost) { 49804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan this(dragHost, new Timer()); 50804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 51804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 52804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @VisibleForTesting 53804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan protected ItemDragListener(H dragHost, Timer timer) { 54804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost = dragHost; 55804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mHoverTimer = timer; 56804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 57804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 58804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @Override 59804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public boolean onDrag(final View v, DragEvent event) { 60804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan switch (event.getAction()) { 61804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_STARTED: 62804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 63804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_ENTERED: 645a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin handleEnteredEvent(v, event); 65804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 66804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_LOCATION: 6757facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan handleLocationEvent(v, event.getX(), event.getY()); 68804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 69804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_EXITED: 70804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_ENDED: 71804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan handleExitedEndedEvent(v); 72804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 73804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DROP: 74804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return handleDropEvent(v, event); 75804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 76804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 77804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return false; 78804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 79804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 805a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin private void handleEnteredEvent(View v, DragEvent event) { 817f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin mDragHost.onDragEntered(v); 825a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin @Nullable TimerTask task = createOpenTask(v, event); 835a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin if (task == null) { 845a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin return; 855a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin } 86804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost.setDropTargetHighlight(v, true); 87804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan v.setTag(R.id.drag_hovering_tag, task); 88a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan mHoverTimer.schedule(task, SPRING_TIMEOUT); 89804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 90804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 9157facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan private void handleLocationEvent(View v, float x, float y) { 9257facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan Drawable background = v.getBackground(); 9357facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan if (background != null) { 9457facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan background.setHotspot(x, y); 9557facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan } 9657facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan } 9757facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan 98804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan private void handleExitedEndedEvent(View v) { 99804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost.setDropTargetHighlight(v, false); 100804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 101804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag); 102804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan if (task != null) { 103804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan task.cancel(); 104804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 105804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 106804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 107804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan private boolean handleDropEvent(View v, DragEvent event) { 108804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan ClipData clipData = event.getClipData(); 109804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan if (clipData == null) { 110804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring."); 111804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return false; 112804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 113804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 114804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return handleDropEventChecked(v, event); 115804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 116804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 1175a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin /** 1185a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin * Sub-classes such as {@link DirectoryDragListener} can override this method and return null. 1195a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin */ 1205a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { 121804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan TimerTask task = new TimerTask() { 122804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @Override 123804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public void run() { 124804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost.runOnUiThread(() -> { 125804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost.onViewHovered(v); 126804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan }); 127804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 128804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan }; 129804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return task; 130804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 131804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 132804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 133804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Handles a drop event. Override it if you want to do something on drop event. It's called when 134804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null. 135804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * 136804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param v The view where user drops. 137804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param event the drag event. 138804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @return true if this event is consumed; false otherwise 139804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 140804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public boolean handleDropEventChecked(View v, DragEvent event) { 141804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return false; // we didn't handle the drop 142804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 143804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 144804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 145804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * An interface {@link ItemDragListener} uses to make some callbacks. 146804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 147804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public interface DragHost { 148804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 149804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 150804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Runs this runnable in main thread. 151804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 152804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan void runOnUiThread(Runnable runnable); 153804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 154804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 155804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Highlights/unhighlights the view to visually indicate this view is being hovered. 156804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param v the view being hovered 157804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param highlight true if highlight the view; false if unhighlight it 158804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 159804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan void setDropTargetHighlight(View v, boolean highlight); 160804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 161804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 162804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Notifies hovering timeout has elapsed 163804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param v the view being hovered 164804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 165804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan void onViewHovered(View v); 1667f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin 1677f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin /** 1687f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin * Notifies right away when drag shadow enters the view 1697f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin * @param v the view which drop shadow just entered 1707f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin */ 1717f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin void onDragEntered(View v); 172804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 173804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan} 174