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