OSDN Git Service

Adding support for showing the widget preview based on the provided RemoteViews
[android-x86/packages-apps-Launcher3.git] / src / com / android / launcher3 / dragndrop / PinItemDragListener.java
1 /*
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
17 package com.android.launcher3.dragndrop;
18
19 import android.appwidget.AppWidgetManager;
20 import android.content.ClipDescription;
21 import android.graphics.Point;
22 import android.graphics.Rect;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.SystemClock;
29 import android.util.Log;
30 import android.view.DragEvent;
31 import android.view.View;
32 import android.widget.RemoteViews;
33
34 import com.android.launcher3.DeleteDropTarget;
35 import com.android.launcher3.DragSource;
36 import com.android.launcher3.DropTarget;
37 import com.android.launcher3.ItemInfo;
38 import com.android.launcher3.Launcher;
39 import com.android.launcher3.LauncherAppWidgetProviderInfo;
40 import com.android.launcher3.PendingAddItemInfo;
41 import com.android.launcher3.R;
42 import com.android.launcher3.compat.PinItemRequestCompat;
43 import com.android.launcher3.folder.Folder;
44 import com.android.launcher3.userevent.nano.LauncherLogProto;
45 import com.android.launcher3.widget.PendingAddShortcutInfo;
46 import com.android.launcher3.widget.PendingAddWidgetInfo;
47 import com.android.launcher3.widget.PendingItemDragHelper;
48 import com.android.launcher3.widget.WidgetAddFlowHandler;
49
50 import java.util.UUID;
51
52 /**
53  * {@link DragSource} for handling drop from a different window. This object is initialized
54  * in the source window and is passed on to the Launcher activity as an Intent extra.
55  */
56 public class PinItemDragListener
57         implements Parcelable, View.OnDragListener, DragSource, DragOptions.PreDragCondition {
58
59     private static final String TAG = "PinItemDragListener";
60
61     private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
62     public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
63
64     private final PinItemRequestCompat mRequest;
65
66     // Position of preview relative to the touch location
67     private final Rect mPreviewRect;
68
69     private final int mPreviewBitmapWidth;
70     private final int mPreviewViewWidth;
71
72     // Randomly generated id used to verify the drag event.
73     private final String mId;
74
75     private Launcher mLauncher;
76     private DragController mDragController;
77     private long mDragStartTime;
78
79     public PinItemDragListener(PinItemRequestCompat request, Rect previewRect,
80             int previewBitmapWidth, int previewViewWidth) {
81         mRequest = request;
82         mPreviewRect = previewRect;
83         mPreviewBitmapWidth = previewBitmapWidth;
84         mPreviewViewWidth = previewViewWidth;
85         mId = UUID.randomUUID().toString();
86     }
87
88     private PinItemDragListener(Parcel parcel) {
89         mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
90         mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
91         mPreviewBitmapWidth = parcel.readInt();
92         mPreviewViewWidth = parcel.readInt();
93         mId = parcel.readString();
94     }
95
96     public String getMimeType() {
97         return MIME_TYPE_PREFIX + mId;
98     }
99
100     @Override
101     public int describeContents() {
102         return 0;
103     }
104
105     @Override
106     public void writeToParcel(Parcel parcel, int i) {
107         mRequest.writeToParcel(parcel, i);
108         mPreviewRect.writeToParcel(parcel, i);
109         parcel.writeInt(mPreviewBitmapWidth);
110         parcel.writeInt(mPreviewViewWidth);
111         parcel.writeString(mId);
112     }
113
114     public void setLauncher(Launcher launcher) {
115         mLauncher = launcher;
116         mDragController = launcher.getDragController();
117     }
118
119     @Override
120     public boolean onDrag(View view, DragEvent event) {
121         if (mLauncher == null || mDragController == null) {
122             postCleanup();
123             return false;
124         }
125         if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
126             if (onDragStart(event)) {
127                 return true;
128             } else {
129                 postCleanup();
130                 return false;
131             }
132         }
133         return mDragController.onDragEvent(mDragStartTime, event);
134     }
135
136     private boolean onDragStart(DragEvent event) {
137         if (!mRequest.isValid()) {
138             return false;
139         }
140         ClipDescription desc =  event.getClipDescription();
141         if (desc == null || !desc.hasMimeType(getMimeType())) {
142             Log.e(TAG, "Someone started a dragAndDrop before us.");
143             return false;
144         }
145
146         final PendingAddItemInfo item;
147         if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
148             item = new PendingAddShortcutInfo(
149                     new PinShortcutRequestActivityInfo(mRequest, mLauncher));
150         } else {
151             // mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET
152             LauncherAppWidgetProviderInfo providerInfo =
153                     LauncherAppWidgetProviderInfo.fromProviderInfo(
154                             mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher));
155             final PinWidgetFlowHandler flowHandler =
156                     new PinWidgetFlowHandler(providerInfo, mRequest);
157             item = new PendingAddWidgetInfo(providerInfo) {
158                 @Override
159                 public WidgetAddFlowHandler getHandler() {
160                     return flowHandler;
161                 }
162             };
163         }
164         View view = new View(mLauncher);
165         view.setTag(item);
166
167         Point downPos = new Point((int) event.getX(), (int) event.getY());
168         DragOptions options = new DragOptions();
169         options.systemDndStartPoint = downPos;
170         options.preDragCondition = this;
171
172         // We use drag event position as the screenPos for the preview image. Since mPreviewRect
173         // already includes the view position relative to the drag event on the source window,
174         // and the absolute position (position relative to the screen) of drag event is same
175         // across windows, using drag position here give a good estimate for relative position
176         // to source window.
177         PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
178         if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET) {
179             dragHelper.setPreview(getPreview(mRequest));
180         }
181
182         dragHelper.startDrag(new Rect(mPreviewRect),
183                 mPreviewBitmapWidth, mPreviewViewWidth, downPos,  this, options);
184         mDragStartTime = SystemClock.uptimeMillis();
185         return true;
186     }
187
188     @Override
189     public boolean shouldStartDrag(double distanceDragged) {
190         // Stay in pre-drag mode, if workspace is locked.
191         return !mLauncher.isWorkspaceLocked();
192     }
193
194     @Override
195     public void onPreDragStart(DropTarget.DragObject dragObject) {
196         // The predrag starts when the workspace is not yet loaded. In some cases we set
197         // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
198         // dragView from being visible. Instead just skip the fade-in animation here.
199         mLauncher.getDragLayer().setAlpha(1);
200
201         dragObject.dragView.setColor(
202                 mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
203     }
204
205     @Override
206     public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
207         if (dragStarted) {
208             dragObject.dragView.setColor(0);
209         }
210     }
211
212     @Override
213     public boolean supportsAppInfoDropTarget() {
214         return false;
215     }
216
217     @Override
218     public boolean supportsDeleteDropTarget() {
219         return false;
220     }
221
222     @Override
223     public float getIntrinsicIconScaleFactor() {
224         return 1f;
225     }
226
227     @Override
228     public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
229             boolean success) {
230         if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
231                 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
232             // Exit spring loaded mode if we have not successfully dropped or have not handled the
233             // drop in Workspace
234             mLauncher.exitSpringLoadedDragModeDelayed(true,
235                     Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
236         }
237         postCleanup();
238     }
239
240     @Override
241     public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
242             LauncherLogProto.Target targetParent) {
243         // TODO: We should probably log something
244     }
245
246     private void postCleanup() {
247         new Handler(Looper.getMainLooper()).post(new Runnable() {
248             @Override
249             public void run() {
250                 removeListener();
251             }
252         });
253     }
254
255     public void removeListener() {
256         if (mLauncher != null) {
257             mLauncher.getDragLayer().setOnDragListener(null);
258         }
259     }
260
261     public static RemoteViews getPreview(PinItemRequestCompat request) {
262         Bundle extras = request.getExtras();
263         if (extras != null &&
264                 extras.get(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW) instanceof RemoteViews) {
265             return (RemoteViews) extras.get(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW);
266         }
267         return null;
268     }
269
270     public static final Parcelable.Creator<PinItemDragListener> CREATOR =
271             new Parcelable.Creator<PinItemDragListener>() {
272                 public PinItemDragListener createFromParcel(Parcel source) {
273                     return new PinItemDragListener(source);
274                 }
275
276                 public PinItemDragListener[] newArray(int size) {
277                     return new PinItemDragListener[size];
278                 }
279             };
280 }