blob: cee5e91490cd67a4009a24d5a7dd073afc14692d [file] [log] [blame]
Garfield, Tan804133e2016-04-20 15:13:56 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.documentsui;
18
19import android.content.ClipData;
Garfield, Tan57facaf72016-05-27 15:02:35 -070020import android.graphics.drawable.Drawable;
Garfield, Tan804133e2016-04-20 15:13:56 -070021import android.util.Log;
22import android.view.DragEvent;
23import android.view.View;
24import android.view.View.OnDragListener;
Garfield, Tan804133e2016-04-20 15:13:56 -070025
26import com.android.documentsui.ItemDragListener.DragHost;
27import com.android.internal.annotations.VisibleForTesting;
28
29import java.util.Timer;
30import java.util.TimerTask;
31
Ben Lin5a305b42016-09-08 11:33:07 -070032import javax.annotation.Nullable;
33
Garfield, Tan804133e2016-04-20 15:13:56 -070034/**
35 * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want
36 * items to pop-open when user hovers on them during a drag n drop.
37 */
38public class ItemDragListener<H extends DragHost> implements OnDragListener {
39
40 private static final String TAG = "ItemDragListener";
41
42 @VisibleForTesting
Ben Linb1ab6962017-03-14 10:48:57 -070043 static final int DEFAULT_SPRING_TIMEOUT = 1500;
Garfield, Tan804133e2016-04-20 15:13:56 -070044
45 protected final H mDragHost;
46 private final Timer mHoverTimer;
Ben Linb1ab6962017-03-14 10:48:57 -070047 private final int mSpringTimeout;
Garfield, Tan804133e2016-04-20 15:13:56 -070048
49 public ItemDragListener(H dragHost) {
Ben Linb1ab6962017-03-14 10:48:57 -070050 this(dragHost, new Timer(), DEFAULT_SPRING_TIMEOUT);
51 }
52
53 public ItemDragListener(H dragHost, int springTimeout) {
54 this(dragHost, new Timer(), springTimeout);
Garfield, Tan804133e2016-04-20 15:13:56 -070055 }
56
57 @VisibleForTesting
Ben Linb1ab6962017-03-14 10:48:57 -070058 protected ItemDragListener(H dragHost, Timer timer, int springTimeout) {
Garfield, Tan804133e2016-04-20 15:13:56 -070059 mDragHost = dragHost;
60 mHoverTimer = timer;
Ben Linb1ab6962017-03-14 10:48:57 -070061 mSpringTimeout = springTimeout;
Garfield, Tan804133e2016-04-20 15:13:56 -070062 }
63
64 @Override
65 public boolean onDrag(final View v, DragEvent event) {
66 switch (event.getAction()) {
67 case DragEvent.ACTION_DRAG_STARTED:
68 return true;
69 case DragEvent.ACTION_DRAG_ENTERED:
Ben Lin5a305b42016-09-08 11:33:07 -070070 handleEnteredEvent(v, event);
Garfield, Tan804133e2016-04-20 15:13:56 -070071 return true;
72 case DragEvent.ACTION_DRAG_LOCATION:
Garfield, Tan57facaf72016-05-27 15:02:35 -070073 handleLocationEvent(v, event.getX(), event.getY());
Garfield, Tan804133e2016-04-20 15:13:56 -070074 return true;
75 case DragEvent.ACTION_DRAG_EXITED:
Garfield Tanda2c0f02017-04-11 13:47:58 -070076 mDragHost.onDragExited(v);
77 handleExitedEndedEvent(v, event);
78 return true;
Garfield, Tan804133e2016-04-20 15:13:56 -070079 case DragEvent.ACTION_DRAG_ENDED:
Garfield Tanda2c0f02017-04-11 13:47:58 -070080 mDragHost.onDragEnded();
Ben Lin166c5c62016-11-01 12:14:38 -070081 handleExitedEndedEvent(v, event);
Garfield, Tan804133e2016-04-20 15:13:56 -070082 return true;
83 case DragEvent.ACTION_DROP:
84 return handleDropEvent(v, event);
85 }
86
87 return false;
88 }
89
Ben Lin5a305b42016-09-08 11:33:07 -070090 private void handleEnteredEvent(View v, DragEvent event) {
Garfield Tanda2c0f02017-04-11 13:47:58 -070091 mDragHost.onDragEntered(v);
Ben Lin5a305b42016-09-08 11:33:07 -070092 @Nullable TimerTask task = createOpenTask(v, event);
Garfield Tanda2c0f02017-04-11 13:47:58 -070093 mDragHost.setDropTargetHighlight(v, true);
Ben Lin5a305b42016-09-08 11:33:07 -070094 if (task == null) {
95 return;
96 }
Garfield, Tan804133e2016-04-20 15:13:56 -070097 v.setTag(R.id.drag_hovering_tag, task);
Ben Linb1ab6962017-03-14 10:48:57 -070098 mHoverTimer.schedule(task, mSpringTimeout);
Garfield, Tan804133e2016-04-20 15:13:56 -070099 }
100
Garfield, Tan57facaf72016-05-27 15:02:35 -0700101 private void handleLocationEvent(View v, float x, float y) {
102 Drawable background = v.getBackground();
103 if (background != null) {
104 background.setHotspot(x, y);
105 }
106 }
107
Ben Lin166c5c62016-11-01 12:14:38 -0700108 private void handleExitedEndedEvent(View v, DragEvent event) {
Garfield Tanda2c0f02017-04-11 13:47:58 -0700109 mDragHost.setDropTargetHighlight(v, false);
Garfield, Tan804133e2016-04-20 15:13:56 -0700110 TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag);
111 if (task != null) {
112 task.cancel();
113 }
114 }
115
116 private boolean handleDropEvent(View v, DragEvent event) {
117 ClipData clipData = event.getClipData();
118 if (clipData == null) {
119 Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring.");
120 return false;
121 }
122
123 return handleDropEventChecked(v, event);
124 }
125
Ben Lin5a305b42016-09-08 11:33:07 -0700126 /**
127 * Sub-classes such as {@link DirectoryDragListener} can override this method and return null.
128 */
129 public @Nullable TimerTask createOpenTask(final View v, DragEvent event) {
Garfield, Tan804133e2016-04-20 15:13:56 -0700130 TimerTask task = new TimerTask() {
131 @Override
132 public void run() {
133 mDragHost.runOnUiThread(() -> {
134 mDragHost.onViewHovered(v);
135 });
136 }
137 };
138 return task;
139 }
140
141 /**
142 * Handles a drop event. Override it if you want to do something on drop event. It's called when
143 * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null.
144 *
145 * @param v The view where user drops.
146 * @param event the drag event.
147 * @return true if this event is consumed; false otherwise
148 */
149 public boolean handleDropEventChecked(View v, DragEvent event) {
150 return false; // we didn't handle the drop
151 }
152
153 /**
154 * An interface {@link ItemDragListener} uses to make some callbacks.
155 */
156 public interface DragHost {
157
158 /**
159 * Runs this runnable in main thread.
160 */
161 void runOnUiThread(Runnable runnable);
162
163 /**
164 * Highlights/unhighlights the view to visually indicate this view is being hovered.
Garfield Tanda2c0f02017-04-11 13:47:58 -0700165 *
166 * Called after {@link #onDragEntered(View)}, {@link #onDragExited(View)}
167 * or {@link #onDragEnded()}.
168 *
Garfield, Tan804133e2016-04-20 15:13:56 -0700169 * @param v the view being hovered
170 * @param highlight true if highlight the view; false if unhighlight it
171 */
Garfield Tanda2c0f02017-04-11 13:47:58 -0700172 void setDropTargetHighlight(View v, boolean highlight);
Garfield, Tan804133e2016-04-20 15:13:56 -0700173
174 /**
175 * Notifies hovering timeout has elapsed
176 * @param v the view being hovered
177 */
178 void onViewHovered(View v);
Ben Lin7f72a3c2016-09-27 16:37:28 -0700179
180 /**
181 * Notifies right away when drag shadow enters the view
182 * @param v the view which drop shadow just entered
183 */
Garfield Tanda2c0f02017-04-11 13:47:58 -0700184 void onDragEntered(View v);
Ben Lind0202122016-11-10 18:00:12 -0800185
186 /**
187 * Notifies right away when drag shadow exits the view
188 * @param v the view which drop shadow just exited
Ben Lind0202122016-11-10 18:00:12 -0800189 */
Garfield Tanda2c0f02017-04-11 13:47:58 -0700190 void onDragExited(View v);
191
192 /**
193 * Notifies when the drag and drop has ended.
194 */
195 void onDragEnded();
Garfield, Tan804133e2016-04-20 15:13:56 -0700196 }
197}