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 43b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin static final int DEFAULT_SPRING_TIMEOUT = 1500; 44804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 45804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan protected final H mDragHost; 46804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan private final Timer mHoverTimer; 47b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin private final int mSpringTimeout; 48804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 49804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public ItemDragListener(H dragHost) { 50b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin this(dragHost, new Timer(), DEFAULT_SPRING_TIMEOUT); 51b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin } 52b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin 53b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin public ItemDragListener(H dragHost, int springTimeout) { 54b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin this(dragHost, new Timer(), springTimeout); 55804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 56804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 57804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @VisibleForTesting 58b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin protected ItemDragListener(H dragHost, Timer timer, int springTimeout) { 59804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost = dragHost; 60804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mHoverTimer = timer; 61b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin mSpringTimeout = springTimeout; 62804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 63804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 64804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @Override 65804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public boolean onDrag(final View v, DragEvent event) { 66804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan switch (event.getAction()) { 67804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_STARTED: 68804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 69804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_ENTERED: 705a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin handleEnteredEvent(v, event); 71804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 72804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_LOCATION: 7357facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan handleLocationEvent(v, event.getX(), event.getY()); 74804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 75804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_EXITED: 76da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan mDragHost.onDragExited(v); 77da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan handleExitedEndedEvent(v, event); 78da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan return true; 79804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DRAG_ENDED: 80da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan mDragHost.onDragEnded(); 81166c5c6fc17461cb52f2b83eaec86cfc15b53004Ben Lin handleExitedEndedEvent(v, event); 82804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return true; 83804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan case DragEvent.ACTION_DROP: 84804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return handleDropEvent(v, event); 85804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 86804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 87804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return false; 88804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 89804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 905a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin private void handleEnteredEvent(View v, DragEvent event) { 91da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan mDragHost.onDragEntered(v); 925a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin @Nullable TimerTask task = createOpenTask(v, event); 93da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan mDragHost.setDropTargetHighlight(v, true); 945a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin if (task == null) { 955a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin return; 965a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin } 97804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan v.setTag(R.id.drag_hovering_tag, task); 98b1ab696d110e25a09ac2e9d1af1be3c7c8b1444dBen Lin mHoverTimer.schedule(task, mSpringTimeout); 99804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 100804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 10157facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan private void handleLocationEvent(View v, float x, float y) { 10257facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan Drawable background = v.getBackground(); 10357facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan if (background != null) { 10457facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan background.setHotspot(x, y); 10557facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan } 10657facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan } 10757facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan 108166c5c6fc17461cb52f2b83eaec86cfc15b53004Ben Lin private void handleExitedEndedEvent(View v, DragEvent event) { 109da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan mDragHost.setDropTargetHighlight(v, false); 110804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag); 111804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan if (task != null) { 112804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan task.cancel(); 113804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 114804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 115804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 116804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan private boolean handleDropEvent(View v, DragEvent event) { 117804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan ClipData clipData = event.getClipData(); 118804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan if (clipData == null) { 119804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring."); 120804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return false; 121804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 122804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 123804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return handleDropEventChecked(v, event); 124804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 125804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 1265a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin /** 1275a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin * Sub-classes such as {@link DirectoryDragListener} can override this method and return null. 1285a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin */ 1295a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { 130804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan TimerTask task = new TimerTask() { 131804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan @Override 132804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public void run() { 133804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost.runOnUiThread(() -> { 134804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan mDragHost.onViewHovered(v); 135804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan }); 136804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 137804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan }; 138804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return task; 139804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 140804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 141804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 142804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Handles a drop event. Override it if you want to do something on drop event. It's called when 143804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null. 144804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * 145804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param v The view where user drops. 146804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param event the drag event. 147804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @return true if this event is consumed; false otherwise 148804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 149804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public boolean handleDropEventChecked(View v, DragEvent event) { 150804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan return false; // we didn't handle the drop 151804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 152804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 153804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 154804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * An interface {@link ItemDragListener} uses to make some callbacks. 155804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 156804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan public interface DragHost { 157804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 158804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 159804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Runs this runnable in main thread. 160804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 161804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan void runOnUiThread(Runnable runnable); 162804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 163804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 164804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Highlights/unhighlights the view to visually indicate this view is being hovered. 165da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan * 166da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan * Called after {@link #onDragEntered(View)}, {@link #onDragExited(View)} 167da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan * or {@link #onDragEnded()}. 168da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan * 169804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param v the view being hovered 170804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param highlight true if highlight the view; false if unhighlight it 171804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 172da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan void setDropTargetHighlight(View v, boolean highlight); 173804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan 174804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan /** 175804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Notifies hovering timeout has elapsed 176804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * @param v the view being hovered 177804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */ 178804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan void onViewHovered(View v); 1797f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin 1807f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin /** 1817f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin * Notifies right away when drag shadow enters the view 1827f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin * @param v the view which drop shadow just entered 1837f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin */ 184da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan void onDragEntered(View v); 185d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin 186d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin /** 187d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin * Notifies right away when drag shadow exits the view 188d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin * @param v the view which drop shadow just exited 189d020212fdad3be1537dfa47ff5b67e3bc4272d5eBen Lin */ 190da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan void onDragExited(View v); 191da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan 192da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan /** 193da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan * Notifies when the drag and drop has ended. 194da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan */ 195da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan void onDragEnded(); 196804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan } 197804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan} 198