2 * Copyright (C) 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.launcher3.dragndrop;
19 import android.appwidget.AppWidgetManager;
20 import android.content.ClipDescription;
21 import android.content.Intent;
22 import android.graphics.Point;
23 import android.graphics.Rect;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.os.SystemClock;
30 import android.util.Log;
31 import android.view.DragEvent;
32 import android.view.View;
33 import android.widget.RemoteViews;
35 import com.android.launcher3.DeleteDropTarget;
36 import com.android.launcher3.DragSource;
37 import com.android.launcher3.DropTarget;
38 import com.android.launcher3.ItemInfo;
39 import com.android.launcher3.Launcher;
40 import com.android.launcher3.LauncherAppWidgetProviderInfo;
41 import com.android.launcher3.PendingAddItemInfo;
42 import com.android.launcher3.R;
43 import com.android.launcher3.compat.PinItemRequestCompat;
44 import com.android.launcher3.folder.Folder;
45 import com.android.launcher3.userevent.nano.LauncherLogProto;
46 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
47 import com.android.launcher3.widget.PendingAddShortcutInfo;
48 import com.android.launcher3.widget.PendingAddWidgetInfo;
49 import com.android.launcher3.widget.PendingItemDragHelper;
50 import com.android.launcher3.widget.WidgetAddFlowHandler;
52 import java.util.UUID;
55 * {@link DragSource} for handling drop from a different window. This object is initialized
56 * in the source window and is passed on to the Launcher activity as an Intent extra.
58 public class PinItemDragListener
59 implements Parcelable, View.OnDragListener, DragSource, DragOptions.PreDragCondition {
61 private static final String TAG = "PinItemDragListener";
63 private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
64 public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
66 private final PinItemRequestCompat mRequest;
68 // Position of preview relative to the touch location
69 private final Rect mPreviewRect;
71 private final int mPreviewBitmapWidth;
72 private final int mPreviewViewWidth;
74 // Randomly generated id used to verify the drag event.
75 private final String mId;
77 private Launcher mLauncher;
78 private DragController mDragController;
79 private long mDragStartTime;
81 public PinItemDragListener(PinItemRequestCompat request, Rect previewRect,
82 int previewBitmapWidth, int previewViewWidth) {
84 mPreviewRect = previewRect;
85 mPreviewBitmapWidth = previewBitmapWidth;
86 mPreviewViewWidth = previewViewWidth;
87 mId = UUID.randomUUID().toString();
90 private PinItemDragListener(Parcel parcel) {
91 mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
92 mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
93 mPreviewBitmapWidth = parcel.readInt();
94 mPreviewViewWidth = parcel.readInt();
95 mId = parcel.readString();
98 public String getMimeType() {
99 return MIME_TYPE_PREFIX + mId;
103 public int describeContents() {
108 public void writeToParcel(Parcel parcel, int i) {
109 mRequest.writeToParcel(parcel, i);
110 mPreviewRect.writeToParcel(parcel, i);
111 parcel.writeInt(mPreviewBitmapWidth);
112 parcel.writeInt(mPreviewViewWidth);
113 parcel.writeString(mId);
116 public void setLauncher(Launcher launcher) {
117 mLauncher = launcher;
118 mDragController = launcher.getDragController();
122 public boolean onDrag(View view, DragEvent event) {
123 if (mLauncher == null || mDragController == null) {
127 if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
128 if (onDragStart(event)) {
135 return mDragController.onDragEvent(mDragStartTime, event);
138 private boolean onDragStart(DragEvent event) {
139 if (!mRequest.isValid()) {
142 ClipDescription desc = event.getClipDescription();
143 if (desc == null || !desc.hasMimeType(getMimeType())) {
144 Log.e(TAG, "Someone started a dragAndDrop before us.");
148 final PendingAddItemInfo item;
149 if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
150 item = new PendingAddShortcutInfo(
151 new PinShortcutRequestActivityInfo(mRequest, mLauncher));
153 // mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET
154 LauncherAppWidgetProviderInfo providerInfo =
155 LauncherAppWidgetProviderInfo.fromProviderInfo(
156 mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher));
157 final PinWidgetFlowHandler flowHandler =
158 new PinWidgetFlowHandler(providerInfo, mRequest);
159 item = new PendingAddWidgetInfo(providerInfo) {
161 public WidgetAddFlowHandler getHandler() {
166 View view = new View(mLauncher);
169 Point downPos = new Point((int) event.getX(), (int) event.getY());
170 DragOptions options = new DragOptions();
171 options.systemDndStartPoint = downPos;
172 options.preDragCondition = this;
174 // We use drag event position as the screenPos for the preview image. Since mPreviewRect
175 // already includes the view position relative to the drag event on the source window,
176 // and the absolute position (position relative to the screen) of drag event is same
177 // across windows, using drag position here give a good estimate for relative position
179 PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
180 if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET) {
181 dragHelper.setPreview(getPreview(mRequest));
184 dragHelper.startDrag(new Rect(mPreviewRect),
185 mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
186 mDragStartTime = SystemClock.uptimeMillis();
191 public boolean shouldStartDrag(double distanceDragged) {
192 // Stay in pre-drag mode, if workspace is locked.
193 return !mLauncher.isWorkspaceLocked();
197 public void onPreDragStart(DropTarget.DragObject dragObject) {
198 // The predrag starts when the workspace is not yet loaded. In some cases we set
199 // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
200 // dragView from being visible. Instead just skip the fade-in animation here.
201 mLauncher.getDragLayer().setAlpha(1);
203 dragObject.dragView.setColor(
204 mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
208 public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
210 dragObject.dragView.setColor(0);
215 public boolean supportsAppInfoDropTarget() {
220 public boolean supportsDeleteDropTarget() {
225 public float getIntrinsicIconScaleFactor() {
230 public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
232 if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
233 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
234 // Exit spring loaded mode if we have not successfully dropped or have not handled the
236 mLauncher.exitSpringLoadedDragModeDelayed(true,
237 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
243 public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
244 LauncherLogProto.Target targetParent) {
245 targetParent.containerType = ContainerType.PINITEM;
248 private void postCleanup() {
249 if (mLauncher != null) {
250 // Remove any drag params from the launcher intent since the drag operation is complete.
251 Intent newIntent = new Intent(mLauncher.getIntent());
252 newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
253 mLauncher.setIntent(newIntent);
256 new Handler(Looper.getMainLooper()).post(new Runnable() {
264 public void removeListener() {
265 if (mLauncher != null) {
266 mLauncher.getDragLayer().setOnDragListener(null);
270 public static RemoteViews getPreview(PinItemRequestCompat request) {
271 Bundle extras = request.getExtras();
272 if (extras != null &&
273 extras.get(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW) instanceof RemoteViews) {
274 return (RemoteViews) extras.get(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW);
279 public static boolean handleDragRequest(Launcher launcher, Intent intent) {
280 if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) {
283 Parcelable dragExtra = intent.getParcelableExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
284 if (dragExtra instanceof PinItemDragListener) {
285 PinItemDragListener dragListener = (PinItemDragListener) dragExtra;
286 dragListener.setLauncher(launcher);
288 launcher.getDragLayer().setOnDragListener(dragListener);
294 public static final Parcelable.Creator<PinItemDragListener> CREATOR =
295 new Parcelable.Creator<PinItemDragListener>() {
296 public PinItemDragListener createFromParcel(Parcel source) {
297 return new PinItemDragListener(source);
300 public PinItemDragListener[] newArray(int size) {
301 return new PinItemDragListener[size];