blob: 783f6d446cabc4d925025ec0f44498bb88d9ab9f [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:
Ben Lind0202122016-11-10 18:00:12 -080076 mDragHost.onDragExited(v, event.getLocalState());
77 // fall through
Garfield, Tan804133e2016-04-20 15:13:56 -070078 case DragEvent.ACTION_DRAG_ENDED:
Ben Lin166c5c62016-11-01 12:14:38 -070079 handleExitedEndedEvent(v, event);
Garfield, Tan804133e2016-04-20 15:13:56 -070080 return true;
81 case DragEvent.ACTION_DROP:
82 return handleDropEvent(v, event);
83 }
84
85 return false;
86 }
87
Ben Lin5a305b42016-09-08 11:33:07 -070088 private void handleEnteredEvent(View v, DragEvent event) {
Ben Lin1c456292016-10-07 16:43:18 -070089 mDragHost.onDragEntered(v, event.getLocalState());
Ben Lin5a305b42016-09-08 11:33:07 -070090 @Nullable TimerTask task = createOpenTask(v, event);
Ben Lin166c5c62016-11-01 12:14:38 -070091 mDragHost.setDropTargetHighlight(v, event.getLocalState(), true);
Ben Lin5a305b42016-09-08 11:33:07 -070092 if (task == null) {
93 return;
94 }
Garfield, Tan804133e2016-04-20 15:13:56 -070095 v.setTag(R.id.drag_hovering_tag, task);
Ben Linb1ab6962017-03-14 10:48:57 -070096 mHoverTimer.schedule(task, mSpringTimeout);
Garfield, Tan804133e2016-04-20 15:13:56 -070097 }
98
Garfield, Tan57facaf72016-05-27 15:02:35 -070099 private void handleLocationEvent(View v, float x, float y) {
100 Drawable background = v.getBackground();
101 if (background != null) {
102 background.setHotspot(x, y);
103 }
104 }
105
Ben Lin166c5c62016-11-01 12:14:38 -0700106 private void handleExitedEndedEvent(View v, DragEvent event) {
107 mDragHost.setDropTargetHighlight(v, event.getLocalState(), false);
Garfield, Tan804133e2016-04-20 15:13:56 -0700108 TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag);
109 if (task != null) {
110 task.cancel();
111 }
112 }
113
114 private boolean handleDropEvent(View v, DragEvent event) {
115 ClipData clipData = event.getClipData();
116 if (clipData == null) {
117 Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring.");
118 return false;
119 }
120
121 return handleDropEventChecked(v, event);
122 }
123
Ben Lin5a305b42016-09-08 11:33:07 -0700124 /**
125 * Sub-classes such as {@link DirectoryDragListener} can override this method and return null.
126 */
127 public @Nullable TimerTask createOpenTask(final View v, DragEvent event) {
Garfield, Tan804133e2016-04-20 15:13:56 -0700128 TimerTask task = new TimerTask() {
129 @Override
130 public void run() {
131 mDragHost.runOnUiThread(() -> {
132 mDragHost.onViewHovered(v);
133 });
134 }
135 };
136 return task;
137 }
138
139 /**
140 * Handles a drop event. Override it if you want to do something on drop event. It's called when
141 * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null.
142 *
143 * @param v The view where user drops.
144 * @param event the drag event.
145 * @return true if this event is consumed; false otherwise
146 */
147 public boolean handleDropEventChecked(View v, DragEvent event) {
148 return false; // we didn't handle the drop
149 }
150
151 /**
152 * An interface {@link ItemDragListener} uses to make some callbacks.
153 */
154 public interface DragHost {
155
156 /**
157 * Runs this runnable in main thread.
158 */
159 void runOnUiThread(Runnable runnable);
160
161 /**
162 * Highlights/unhighlights the view to visually indicate this view is being hovered.
163 * @param v the view being hovered
Ben Lin166c5c62016-11-01 12:14:38 -0700164 * @param localState the Local state object given by DragEvent
Garfield, Tan804133e2016-04-20 15:13:56 -0700165 * @param highlight true if highlight the view; false if unhighlight it
166 */
Ben Lin166c5c62016-11-01 12:14:38 -0700167 void setDropTargetHighlight(View v, Object localState, boolean highlight);
Garfield, Tan804133e2016-04-20 15:13:56 -0700168
169 /**
170 * Notifies hovering timeout has elapsed
171 * @param v the view being hovered
172 */
173 void onViewHovered(View v);
Ben Lin7f72a3c2016-09-27 16:37:28 -0700174
175 /**
176 * Notifies right away when drag shadow enters the view
177 * @param v the view which drop shadow just entered
Ben Lin1c456292016-10-07 16:43:18 -0700178 * @param localState the Local state object given by DragEvent
Ben Lin7f72a3c2016-09-27 16:37:28 -0700179 */
Ben Lin1c456292016-10-07 16:43:18 -0700180 void onDragEntered(View v, Object localState);
Ben Lind0202122016-11-10 18:00:12 -0800181
182 /**
183 * Notifies right away when drag shadow exits the view
184 * @param v the view which drop shadow just exited
185 * @param localState the Local state object given by DragEvent
186 */
187 void onDragExited(View v, Object localState);
Garfield, Tan804133e2016-04-20 15:13:56 -0700188 }
189}