diff options
Diffstat (limited to 'quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java')
-rw-r--r-- | quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java | 306 |
1 files changed, 276 insertions, 30 deletions
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index ee44927ce4..b3a9f8db7c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -15,25 +15,42 @@ */ package com.android.launcher3.taskbar; -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; - import android.content.ClipData; import android.content.ClipDescription; -import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.view.DragEvent; +import android.view.MotionEvent; import android.view.View; +import androidx.annotation.Nullable; + +import com.android.internal.logging.InstanceId; +import com.android.internal.logging.InstanceIdSequence; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; +import com.android.launcher3.accessibility.DragViewStateAnnouncer; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragDriver; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.dragndrop.DragView; +import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.graphics.DragPreviewProvider; +import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ClipDescriptionCompat; import com.android.systemui.shared.system.LauncherAppsCompat; @@ -41,52 +58,213 @@ import com.android.systemui.shared.system.LauncherAppsCompat; /** * Handles long click on Taskbar items to start a system drag and drop operation. */ -public class TaskbarDragController { +public class TaskbarDragController extends DragController<TaskbarActivityContext> { - private final Context mContext; private final int mDragIconSize; + private final int[] mTempXY = new int[2]; + + // Initialized in init. + TaskbarControllers mControllers; + + // Where the initial touch was relative to the dragged icon. + private int mRegistrationX; + private int mRegistrationY; + + private boolean mIsSystemDragInProgress; - public TaskbarDragController(Context context) { - mContext = context; - Resources resources = mContext.getResources(); + public TaskbarDragController(TaskbarActivityContext activity) { + super(activity); + Resources resources = mActivity.getResources(); mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size); } + public void init(TaskbarControllers controllers) { + mControllers = controllers; + } + /** * Attempts to start a system drag and drop operation for the given View, using its tag to * generate the ClipDescription and Intent. * @return Whether {@link View#startDragAndDrop} started successfully. */ - protected boolean startSystemDragOnLongClick(View view) { + protected boolean startDragOnLongClick(View view) { if (!(view instanceof BubbleTextView)) { return false; } BubbleTextView btv = (BubbleTextView) view; - View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { + + mActivity.setTaskbarWindowFullscreen(true); + btv.post(() -> { + startInternalDrag(btv); + btv.getIcon().setIsDisabled(true); + mControllers.taskbarAutohideSuspendController.updateFlag( + TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, true); + }); + return true; + } + + private void startInternalDrag(BubbleTextView btv) { + float iconScale = btv.getIcon().getAnimatedScale(); + + // Clear the pressed state if necessary + btv.clearFocus(); + btv.setPressed(false); + btv.clearPressedBackground(); + + final DragPreviewProvider previewProvider = new DragPreviewProvider(btv); + final Drawable drawable = previewProvider.createDrawable(); + final float scale = previewProvider.getScaleAndPosition(drawable, mTempXY); + int dragLayerX = mTempXY[0]; + int dragLayerY = mTempXY[1]; + + Rect dragRect = new Rect(); + btv.getSourceVisualDragBounds(dragRect); + dragLayerY += dragRect.top; + + DragOptions dragOptions = new DragOptions(); + dragOptions.preDragCondition = new DragOptions.PreDragCondition() { + private DragView mDragView; + + @Override + public boolean shouldStartDrag(double distanceDragged) { + return mDragView != null && mDragView.isAnimationFinished(); + } + + @Override + public void onPreDragStart(DropTarget.DragObject dragObject) { + mDragView = dragObject.dragView; + } + + @Override + public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { + mDragView = null; + } + }; + if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) { + PopupContainerWithArrow<TaskbarActivityContext> popupContainer = + mControllers.taskbarPopupController.showForIcon(btv); + if (popupContainer != null) { + dragOptions.preDragCondition = popupContainer.createPreDragCondition(); + } + } + + startDrag( + drawable, + /* view = */ null, + /* originalView = */ btv, + dragLayerX, + dragLayerY, + (View target, DropTarget.DragObject d, boolean success) -> {} /* DragSource */, + (WorkspaceItemInfo) btv.getTag(), + /* dragVisualizeOffset = */ null, + dragRect, + scale * iconScale, + scale, + dragOptions); + } + + @Override + protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view, + DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source, + ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale, + float dragViewScaleOnDrop, DragOptions options) { + mOptions = options; + + mRegistrationX = mMotionDown.x - dragLayerX; + mRegistrationY = mMotionDown.y - dragLayerY; + + final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left; + final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top; + + mLastDropTarget = null; + + mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext()); + mDragObject.originalView = originalView; + mDragObject.deferDragViewCleanupPostAnimation = false; + + mIsInPreDrag = mOptions.preDragCondition != null + && !mOptions.preDragCondition.shouldStartDrag(0); + + float scalePx = mDragIconSize - dragRegion.width(); + final DragView dragView = mDragObject.dragView = new TaskbarDragView( + mActivity, + drawable, + mRegistrationX, + mRegistrationY, + initialDragViewScale, + dragViewScaleOnDrop, + scalePx); + dragView.setItemInfo(dragInfo); + mDragObject.dragComplete = false; + + mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft); + mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop); + + mDragDriver = DragDriver.create(this, mOptions, /* secondaryEventConsumer = */ ev -> {}); + if (!mOptions.isAccessibleDrag) { + mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView); + } + + mDragObject.dragSource = source; + mDragObject.dragInfo = dragInfo; + mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy(); + + if (dragRegion != null) { + dragView.setDragRegion(new Rect(dragRegion)); + } + + dragView.show(mLastTouch.x, mLastTouch.y); + mDistanceSinceScroll = 0; + + if (!mIsInPreDrag) { + callOnDragStart(); + } else if (mOptions.preDragCondition != null) { + mOptions.preDragCondition.onPreDragStart(mDragObject); + } + + handleMoveEvent(mLastTouch.x, mLastTouch.y); + + return dragView; + } + + @Override + protected void callOnDragStart() { + super.callOnDragStart(); + // Pre-drag has ended, start the global system drag. + AbstractFloatingView.closeAllOpenViews(mActivity); + startSystemDrag((BubbleTextView) mDragObject.originalView); + } + + private void startSystemDrag(BubbleTextView btv) { + View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) { + @Override public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { shadowSize.set(mDragIconSize, mDragIconSize); - // TODO: should be based on last touch point on the icon. - shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2); + // The registration point was taken before the icon scaled to mDragIconSize, so + // offset the registration to where the touch is on the new size. + int offsetX = (mDragIconSize - mDragObject.dragView.getDragRegionWidth()) / 2; + int offsetY = (mDragIconSize - mDragObject.dragView.getDragRegionHeight()) / 2; + shadowTouchPoint.set(mRegistrationX + offsetX, mRegistrationY + offsetY); } @Override public void onDrawShadow(Canvas canvas) { canvas.save(); - float scale = (float) mDragIconSize / btv.getIconSize(); + float scale = mDragObject.dragView.getScaleX(); canvas.scale(scale, scale); - btv.getIcon().draw(canvas); + mDragObject.dragView.draw(canvas); canvas.restore(); } }; - Object tag = view.getTag(); + Object tag = btv.getTag(); ClipDescription clipDescription = null; Intent intent = null; if (tag instanceof WorkspaceItemInfo) { WorkspaceItemInfo item = (WorkspaceItemInfo) tag; - LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class); + LauncherApps launcherApps = mActivity.getSystemService(LauncherApps.class); clipDescription = new ClipDescription(item.title, new String[] { item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT @@ -115,29 +293,97 @@ public class TaskbarDragController { } if (clipDescription != null && intent != null) { + // Need to share the same InstanceId between launcher3 and WM Shell (internal). + InstanceId internalInstanceId = new InstanceIdSequence( + com.android.launcher3.logging.InstanceId.INSTANCE_ID_MAX).newInstanceId(); + com.android.launcher3.logging.InstanceId launcherInstanceId = + new com.android.launcher3.logging.InstanceId(internalInstanceId.getId()); + + intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId); + ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent)); - view.setOnDragListener(getDraggedViewDragListener()); - return view.startDragAndDrop(clipData, shadowBuilder, null /* localState */, - View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE); + if (btv.startDragAndDrop(clipData, shadowBuilder, null /* localState */, + View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE)) { + onSystemDragStarted(); + + mActivity.getStatsLogManager().logger().withItemInfo(mDragObject.dragInfo) + .withInstanceId(launcherInstanceId) + .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED); + } } - return false; } - /** - * Hide the original Taskbar item while it is being dragged. - */ - private View.OnDragListener getDraggedViewDragListener() { - return (view, dragEvent) -> { + private void onSystemDragStarted() { + mIsSystemDragInProgress = true; + mActivity.getDragLayer().setOnDragListener((view, dragEvent) -> { switch (dragEvent.getAction()) { case DragEvent.ACTION_DRAG_STARTED: - view.setVisibility(INVISIBLE); + // Return true to tell system we are interested in events, so we get DRAG_ENDED. return true; case DragEvent.ACTION_DRAG_ENDED: - view.setVisibility(VISIBLE); - view.setOnDragListener(null); + mIsSystemDragInProgress = false; + maybeOnDragEnd(); return true; } return false; - }; + }); + } + + @Override + public boolean isDragging() { + return super.isDragging() || mIsSystemDragInProgress; + } + + private void maybeOnDragEnd() { + if (!isDragging()) { + ((BubbleTextView) mDragObject.originalView).getIcon().setIsDisabled(false); + mControllers.taskbarAutohideSuspendController.updateFlag( + TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false); + } + } + + @Override + protected void callOnDragEnd() { + super.callOnDragEnd(); + maybeOnDragEnd(); + } + + @Override + protected float getX(MotionEvent ev) { + // We will resize to fill the screen while dragging, so use screen coordinates. This ensures + // we start at the correct position even though touch down is on the smaller DragLayer size. + return ev.getRawX(); + } + + @Override + protected float getY(MotionEvent ev) { + // We will resize to fill the screen while dragging, so use screen coordinates. This ensures + // we start at the correct position even though touch down is on the smaller DragLayer size. + return ev.getRawY(); + } + + @Override + protected Point getClampedDragLayerPos(float x, float y) { + // No need to clamp, as we will take up the entire screen. + mTmpPoint.set(Math.round(x), Math.round(y)); + return mTmpPoint; + } + + @Override + protected void exitDrag() { + if (mDragObject != null) { + mActivity.getDragLayer().removeView(mDragObject.dragView); + } + } + + @Override + public void addDropTarget(DropTarget target) { + // No-op as Taskbar currently doesn't support any drop targets internally. + // Note: if we do add internal DropTargets, we'll still need to ignore Folder. + } + + @Override + protected DropTarget getDefaultDropTarget(int[] dropCoordinates) { + return null; } } |