• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.launcher3.LauncherState.NORMAL;
20 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
21 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
22 
23 import android.content.ClipDescription;
24 import android.content.Intent;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.util.Log;
30 import android.view.DragEvent;
31 import android.view.View;
32 
33 import com.android.launcher3.AbstractFloatingView;
34 import com.android.launcher3.DragSource;
35 import com.android.launcher3.DropTarget.DragObject;
36 import com.android.launcher3.Launcher;
37 import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
38 import com.android.launcher3.widget.PendingItemDragHelper;
39 
40 import java.util.UUID;
41 
42 /**
43  * {@link DragSource} for handling drop from a different window.
44  */
45 public abstract class BaseItemDragListener implements View.OnDragListener, DragSource,
46         DragOptions.PreDragCondition, SchedulerCallback<Launcher> {
47 
48     private static final String TAG = "BaseItemDragListener";
49 
50     private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
51     public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
52 
53     // Position of preview relative to the touch location
54     private final Rect mPreviewRect;
55 
56     private final int mPreviewBitmapWidth;
57     private final int mPreviewViewWidth;
58 
59     // Randomly generated id used to verify the drag event.
60     private final String mId;
61 
62     protected Launcher mLauncher;
63     private DragController mDragController;
64 
BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth)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 
getMimeType()72     public String getMimeType() {
73         return MIME_TYPE_PREFIX + mId;
74     }
75 
76     @Override
init(Launcher launcher, boolean alreadyOnHome)77     public boolean init(Launcher launcher, boolean alreadyOnHome) {
78         AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
79         launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */);
80         launcher.getDragLayer().setOnDragListener(this);
81         launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
82 
83         mLauncher = launcher;
84         mDragController = launcher.getDragController();
85         return false;
86     }
87 
88     @Override
onDrag(View view, DragEvent event)89     public boolean onDrag(View view, DragEvent event) {
90         if (mLauncher == null || mDragController == null) {
91             postCleanup();
92             return false;
93         }
94         if (event.getAction() == DragEvent.ACTION_DRAG_STARTED || !mDragController.isDragging()) {
95             if (onDragStart(event)) {
96                 return true;
97             } else {
98                 postCleanup();
99                 return false;
100             }
101         }
102         return mDragController.onDragEvent(event);
103     }
104 
onDragStart(DragEvent event)105     protected boolean onDragStart(DragEvent event) {
106         return onDragStart(event, this);
107     }
108 
onDragStart(DragEvent event, DragOptions.PreDragCondition preDragCondition)109     protected boolean onDragStart(DragEvent event, DragOptions.PreDragCondition preDragCondition) {
110         ClipDescription desc =  event.getClipDescription();
111         if (desc == null || !desc.hasMimeType(getMimeType())) {
112             Log.e(TAG, "Someone started a dragAndDrop before us.");
113             return false;
114         }
115 
116         Point downPos = new Point((int) event.getX(), (int) event.getY());
117         DragOptions options = new DragOptions();
118         options.simulatedDndStartPoint = downPos;
119         options.preDragCondition = preDragCondition;
120 
121         // We use drag event position as the screenPos for the preview image. Since mPreviewRect
122         // already includes the view position relative to the drag event on the source window,
123         // and the absolute position (position relative to the screen) of drag event is same
124         // across windows, using drag position here give a good estimate for relative position
125         // to source window.
126         createDragHelper().startDrag(new Rect(mPreviewRect),
127                 mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
128         return true;
129     }
130 
createDragHelper()131     protected abstract PendingItemDragHelper createDragHelper();
132 
133     @Override
shouldStartDrag(double distanceDragged)134     public boolean shouldStartDrag(double distanceDragged) {
135         // Stay in pre-drag mode, if workspace is locked.
136         return !mLauncher.isWorkspaceLocked();
137     }
138 
139     @Override
onPreDragStart(DragObject dragObject)140     public void onPreDragStart(DragObject dragObject) {
141         // The predrag starts when the workspace is not yet loaded. In some cases we set
142         // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
143         // dragView from being visible. Instead just skip the fade-in animation here.
144         mLauncher.getDragLayer().setAlpha(1);
145         dragObject.dragView.setAlpha(.5f);
146     }
147 
148     @Override
onPreDragEnd(DragObject dragObject, boolean dragStarted)149     public void onPreDragEnd(DragObject dragObject, boolean dragStarted) {
150         if (dragStarted) {
151             dragObject.dragView.setAlpha(1f);
152         }
153     }
154 
155     @Override
onDropCompleted(View target, DragObject d, boolean success)156     public void onDropCompleted(View target, DragObject d, boolean success) {
157         postCleanup();
158     }
159 
postCleanup()160     protected void postCleanup() {
161         if (mLauncher != null) {
162             // Remove any drag params from the launcher intent since the drag operation is complete.
163             Intent newIntent = new Intent(mLauncher.getIntent());
164             newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
165             mLauncher.setIntent(newIntent);
166         }
167 
168         new Handler(Looper.getMainLooper()).post(this::removeListener);
169     }
170 
removeListener()171     public void removeListener() {
172         if (mLauncher != null) {
173             mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
174             mLauncher.getDragLayer().setOnDragListener(null);
175         }
176     }
177 }
178