blob: f966b74ab501b7c2167d6769414b08a98b1f8fb2 [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
Ben Lind0202122016-11-10 18:00:12 -080019import static com.android.documentsui.base.Shared.DEBUG;
20
Garfield, Tan804133e2016-04-20 15:13:56 -070021import android.content.ClipData;
Garfield, Tan57facaf72016-05-27 15:02:35 -070022import android.graphics.drawable.Drawable;
Garfield, Tan804133e2016-04-20 15:13:56 -070023import android.util.Log;
24import android.view.DragEvent;
25import android.view.View;
26import android.view.View.OnDragListener;
Garfield, Tan804133e2016-04-20 15:13:56 -070027
28import com.android.documentsui.ItemDragListener.DragHost;
Ben Lind0202122016-11-10 18:00:12 -080029import com.android.documentsui.base.DocumentInfo;
Garfield, Tan804133e2016-04-20 15:13:56 -070030import com.android.internal.annotations.VisibleForTesting;
31
Ben Lind0202122016-11-10 18:00:12 -080032import java.util.List;
Garfield, Tan804133e2016-04-20 15:13:56 -070033import java.util.Timer;
34import java.util.TimerTask;
35
Ben Lin5a305b42016-09-08 11:33:07 -070036import javax.annotation.Nullable;
37
Garfield, Tan804133e2016-04-20 15:13:56 -070038/**
39 * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want
40 * items to pop-open when user hovers on them during a drag n drop.
41 */
42public class ItemDragListener<H extends DragHost> implements OnDragListener {
43
44 private static final String TAG = "ItemDragListener";
45
46 @VisibleForTesting
Ben Lin7f72a3c2016-09-27 16:37:28 -070047 static final int SPRING_TIMEOUT = 1500;
Garfield, Tan804133e2016-04-20 15:13:56 -070048
49 protected final H mDragHost;
50 private final Timer mHoverTimer;
51
52 public ItemDragListener(H dragHost) {
53 this(dragHost, new Timer());
54 }
55
56 @VisibleForTesting
57 protected ItemDragListener(H dragHost, Timer timer) {
58 mDragHost = dragHost;
59 mHoverTimer = timer;
60 }
61
62 @Override
63 public boolean onDrag(final View v, DragEvent event) {
64 switch (event.getAction()) {
65 case DragEvent.ACTION_DRAG_STARTED:
66 return true;
67 case DragEvent.ACTION_DRAG_ENTERED:
Ben Lin5a305b42016-09-08 11:33:07 -070068 handleEnteredEvent(v, event);
Garfield, Tan804133e2016-04-20 15:13:56 -070069 return true;
70 case DragEvent.ACTION_DRAG_LOCATION:
Garfield, Tan57facaf72016-05-27 15:02:35 -070071 handleLocationEvent(v, event.getX(), event.getY());
Garfield, Tan804133e2016-04-20 15:13:56 -070072 return true;
73 case DragEvent.ACTION_DRAG_EXITED:
Ben Lind0202122016-11-10 18:00:12 -080074 mDragHost.onDragExited(v, event.getLocalState());
75 // fall through
Garfield, Tan804133e2016-04-20 15:13:56 -070076 case DragEvent.ACTION_DRAG_ENDED:
Ben Lin166c5c62016-11-01 12:14:38 -070077 handleExitedEndedEvent(v, event);
Garfield, Tan804133e2016-04-20 15:13:56 -070078 return true;
79 case DragEvent.ACTION_DROP:
80 return handleDropEvent(v, event);
81 }
82
83 return false;
84 }
85
Ben Lin5a305b42016-09-08 11:33:07 -070086 private void handleEnteredEvent(View v, DragEvent event) {
Ben Lin1c456292016-10-07 16:43:18 -070087 mDragHost.onDragEntered(v, event.getLocalState());
Ben Lin5a305b42016-09-08 11:33:07 -070088 @Nullable TimerTask task = createOpenTask(v, event);
Ben Lin166c5c62016-11-01 12:14:38 -070089 mDragHost.setDropTargetHighlight(v, event.getLocalState(), true);
Ben Lin5a305b42016-09-08 11:33:07 -070090 if (task == null) {
91 return;
92 }
Garfield, Tan804133e2016-04-20 15:13:56 -070093 v.setTag(R.id.drag_hovering_tag, task);
Garfield, Tana5588b62016-07-13 09:23:04 -070094 mHoverTimer.schedule(task, SPRING_TIMEOUT);
Garfield, Tan804133e2016-04-20 15:13:56 -070095 }
96
Garfield, Tan57facaf72016-05-27 15:02:35 -070097 private void handleLocationEvent(View v, float x, float y) {
98 Drawable background = v.getBackground();
99 if (background != null) {
100 background.setHotspot(x, y);
101 }
102 }
103
Ben Lin166c5c62016-11-01 12:14:38 -0700104 private void handleExitedEndedEvent(View v, DragEvent event) {
105 mDragHost.setDropTargetHighlight(v, event.getLocalState(), false);
Garfield, Tan804133e2016-04-20 15:13:56 -0700106 TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag);
107 if (task != null) {
108 task.cancel();
109 }
110 }
111
112 private boolean handleDropEvent(View v, DragEvent event) {
113 ClipData clipData = event.getClipData();
114 if (clipData == null) {
115 Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring.");
116 return false;
117 }
118
119 return handleDropEventChecked(v, event);
120 }
121
Ben Lin5a305b42016-09-08 11:33:07 -0700122 /**
123 * Sub-classes such as {@link DirectoryDragListener} can override this method and return null.
124 */
125 public @Nullable TimerTask createOpenTask(final View v, DragEvent event) {
Garfield, Tan804133e2016-04-20 15:13:56 -0700126 TimerTask task = new TimerTask() {
127 @Override
128 public void run() {
129 mDragHost.runOnUiThread(() -> {
130 mDragHost.onViewHovered(v);
131 });
132 }
133 };
134 return task;
135 }
136
137 /**
138 * Handles a drop event. Override it if you want to do something on drop event. It's called when
139 * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null.
140 *
141 * @param v The view where user drops.
142 * @param event the drag event.
143 * @return true if this event is consumed; false otherwise
144 */
145 public boolean handleDropEventChecked(View v, DragEvent event) {
146 return false; // we didn't handle the drop
147 }
148
149 /**
150 * An interface {@link ItemDragListener} uses to make some callbacks.
151 */
152 public interface DragHost {
153
154 /**
155 * Runs this runnable in main thread.
156 */
157 void runOnUiThread(Runnable runnable);
158
159 /**
160 * Highlights/unhighlights the view to visually indicate this view is being hovered.
161 * @param v the view being hovered
Ben Lin166c5c62016-11-01 12:14:38 -0700162 * @param localState the Local state object given by DragEvent
Garfield, Tan804133e2016-04-20 15:13:56 -0700163 * @param highlight true if highlight the view; false if unhighlight it
164 */
Ben Lin166c5c62016-11-01 12:14:38 -0700165 void setDropTargetHighlight(View v, Object localState, boolean highlight);
Garfield, Tan804133e2016-04-20 15:13:56 -0700166
167 /**
168 * Notifies hovering timeout has elapsed
169 * @param v the view being hovered
170 */
171 void onViewHovered(View v);
Ben Lin7f72a3c2016-09-27 16:37:28 -0700172
173 /**
174 * Notifies right away when drag shadow enters the view
175 * @param v the view which drop shadow just entered
Ben Lin1c456292016-10-07 16:43:18 -0700176 * @param localState the Local state object given by DragEvent
Ben Lin7f72a3c2016-09-27 16:37:28 -0700177 */
Ben Lin1c456292016-10-07 16:43:18 -0700178 void onDragEntered(View v, Object localState);
Ben Lind0202122016-11-10 18:00:12 -0800179
180 /**
181 * Notifies right away when drag shadow exits the view
182 * @param v the view which drop shadow just exited
183 * @param localState the Local state object given by DragEvent
184 */
185 void onDragExited(View v, Object localState);
Garfield, Tan804133e2016-04-20 15:13:56 -0700186 }
187}