blob: d0f2629aa7fde2b79ef7bb5b3e59ddafbf6ada06 [file] [log] [blame]
Sunny Goyal027fba32017-06-19 15:30:19 -07001/*
2 * Copyright (C) 2017 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.launcher3.dragndrop;
18
19import android.content.ClipDescription;
20import android.content.Intent;
21import android.graphics.Point;
22import android.graphics.Rect;
23import android.os.Handler;
24import android.os.Looper;
25import android.os.Parcel;
26import android.os.SystemClock;
27import android.util.Log;
28import android.view.DragEvent;
29import android.view.View;
30
31import com.android.launcher3.DeleteDropTarget;
32import com.android.launcher3.DragSource;
33import com.android.launcher3.DropTarget;
34import com.android.launcher3.Launcher;
35import com.android.launcher3.R;
36import com.android.launcher3.folder.Folder;
37import com.android.launcher3.widget.PendingItemDragHelper;
38
39import java.util.UUID;
40
41/**
42 * {@link DragSource} for handling drop from a different window.
43 */
44public abstract class BaseItemDragListener implements
45 View.OnDragListener, DragSource, DragOptions.PreDragCondition {
46
47 private static final String TAG = "BaseItemDragListener";
48
49 private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
50 public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
51
52 // Position of preview relative to the touch location
53 private final Rect mPreviewRect;
54
55 private final int mPreviewBitmapWidth;
56 private final int mPreviewViewWidth;
57
58 // Randomly generated id used to verify the drag event.
59 private final String mId;
60
61 protected Launcher mLauncher;
62 private DragController mDragController;
63 private long mDragStartTime;
64
65 public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) {
66 mPreviewRect = previewRect;
67 mPreviewBitmapWidth = previewBitmapWidth;
68 mPreviewViewWidth = previewViewWidth;
69 mId = UUID.randomUUID().toString();
70 }
71
72 protected BaseItemDragListener(Parcel parcel) {
73 mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
74 mPreviewBitmapWidth = parcel.readInt();
75 mPreviewViewWidth = parcel.readInt();
76 mId = parcel.readString();
77 }
78
79 protected void writeToParcel(Parcel parcel, int i) {
80 mPreviewRect.writeToParcel(parcel, i);
81 parcel.writeInt(mPreviewBitmapWidth);
82 parcel.writeInt(mPreviewViewWidth);
83 parcel.writeString(mId);
84 }
85
86 public String getMimeType() {
87 return MIME_TYPE_PREFIX + mId;
88 }
89
90 public void setLauncher(Launcher launcher) {
91 mLauncher = launcher;
92 mDragController = launcher.getDragController();
93 }
94
95 @Override
96 public boolean onDrag(View view, DragEvent event) {
97 if (mLauncher == null || mDragController == null) {
98 postCleanup();
99 return false;
100 }
101 if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
102 if (onDragStart(event)) {
103 return true;
104 } else {
105 postCleanup();
106 return false;
107 }
108 }
109 return mDragController.onDragEvent(mDragStartTime, event);
110 }
111
112 protected boolean onDragStart(DragEvent event) {
113 ClipDescription desc = event.getClipDescription();
114 if (desc == null || !desc.hasMimeType(getMimeType())) {
115 Log.e(TAG, "Someone started a dragAndDrop before us.");
116 return false;
117 }
118
119 Point downPos = new Point((int) event.getX(), (int) event.getY());
120 DragOptions options = new DragOptions();
121 options.systemDndStartPoint = downPos;
122 options.preDragCondition = this;
123
124 // We use drag event position as the screenPos for the preview image. Since mPreviewRect
125 // already includes the view position relative to the drag event on the source window,
126 // and the absolute position (position relative to the screen) of drag event is same
127 // across windows, using drag position here give a good estimate for relative position
128 // to source window.
129 createDragHelper().startDrag(new Rect(mPreviewRect),
130 mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
131 mDragStartTime = SystemClock.uptimeMillis();
132 return true;
133 }
134
135 protected abstract PendingItemDragHelper createDragHelper();
136
137 @Override
138 public boolean shouldStartDrag(double distanceDragged) {
139 // Stay in pre-drag mode, if workspace is locked.
140 return !mLauncher.isWorkspaceLocked();
141 }
142
143 @Override
144 public void onPreDragStart(DropTarget.DragObject dragObject) {
145 // The predrag starts when the workspace is not yet loaded. In some cases we set
146 // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
147 // dragView from being visible. Instead just skip the fade-in animation here.
148 mLauncher.getDragLayer().setAlpha(1);
149
150 dragObject.dragView.setColor(
151 mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
152 }
153
154 @Override
155 public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
156 if (dragStarted) {
157 dragObject.dragView.setColor(0);
158 }
159 }
160
161 @Override
162 public boolean supportsAppInfoDropTarget() {
163 return false;
164 }
165
166 @Override
167 public boolean supportsDeleteDropTarget() {
168 return false;
169 }
170
171 @Override
172 public float getIntrinsicIconScaleFactor() {
173 return 1f;
174 }
175
176 @Override
177 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
178 boolean success) {
179 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
180 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
181 // Exit spring loaded mode if we have not successfully dropped or have not handled the
182 // drop in Workspace
183 mLauncher.exitSpringLoadedDragModeDelayed(true,
184 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
185 }
186
187 if (!success) {
188 d.deferDragViewCleanupPostAnimation = false;
189 }
190 postCleanup();
191 }
192
193 private void postCleanup() {
194 if (mLauncher != null) {
195 // Remove any drag params from the launcher intent since the drag operation is complete.
196 Intent newIntent = new Intent(mLauncher.getIntent());
197 newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
198 mLauncher.setIntent(newIntent);
199 }
200
201 new Handler(Looper.getMainLooper()).post(new Runnable() {
202 @Override
203 public void run() {
204 removeListener();
205 }
206 });
207 }
208
209 public void removeListener() {
210 if (mLauncher != null) {
211 mLauncher.getDragLayer().setOnDragListener(null);
212 }
213 }
214}