Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9378317 authored by Winson Chung's avatar Winson Chung
Browse files

Add basic return animation when drag is canceled

Bug: 179390870
Test: Drag, but don't drop to split an app from the taskbar and
      check that Launcher animates the surface back into place
Change-Id: I726855ff5bdffd1fc58d2839b3cc44b3fdb131e9
parent 40570799
Loading
Loading
Loading
Loading
+117 −4
Original line number Diff line number Diff line
@@ -15,6 +15,11 @@
 */
package com.android.launcher3.taskbar;

import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
@@ -27,7 +32,9 @@ import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;

import androidx.annotation.Nullable;

@@ -39,20 +46,25 @@ import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.anim.Interpolators;
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.folder.FolderIcon;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ClipDescriptionCompat;
import com.android.systemui.shared.system.LauncherAppsCompat;
@@ -66,6 +78,8 @@ import java.util.Arrays;
public class TaskbarDragController extends DragController<TaskbarActivityContext> implements
        TaskbarControllers.LoggableTaskbarController {

    private static boolean DEBUG_DRAG_SHADOW_SURFACE = false;

    private final int mDragIconSize;
    private final int[] mTempXY = new int[2];

@@ -78,6 +92,9 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext

    private boolean mIsSystemDragInProgress;

    // Animation for the drag shadow back into position after an unsuccessful drag
    private ValueAnimator mReturnAnimator;

    public TaskbarDragController(TaskbarActivityContext activity) {
        super(activity);
        Resources resources = mActivity.getResources();
@@ -279,6 +296,9 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
            @Override
            public void onDrawShadow(Canvas canvas) {
                canvas.save();
                if (DEBUG_DRAG_SHADOW_SURFACE) {
                    canvas.drawColor(0xffff0000);
                }
                float scale = mDragObject.dragView.getScaleX();
                canvas.scale(scale, scale);
                mDragObject.dragView.draw(canvas);
@@ -337,8 +357,9 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext

            ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent));
            if (btv.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
                    View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE)) {
                onSystemDragStarted();
                    View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE
                            | View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION)) {
                onSystemDragStarted(btv);

                mActivity.getStatsLogManager().logger().withItemInfo(mDragObject.dragInfo)
                        .withInstanceId(launcherInstanceId)
@@ -347,7 +368,7 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
        }
    }

    private void onSystemDragStarted() {
    private void onSystemDragStarted(BubbleTextView btv) {
        mIsSystemDragInProgress = true;
        mActivity.getDragLayer().setOnDragListener((view, dragEvent) -> {
            switch (dragEvent.getAction()) {
@@ -356,7 +377,12 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
                    return true;
                case DragEvent.ACTION_DRAG_ENDED:
                    mIsSystemDragInProgress = false;
                    if (dragEvent.getResult()) {
                        maybeOnDragEnd();
                    } else {
                        // This will take care of calling maybeOnDragEnd() after the animation
                        animateGlobalDragViewToOriginalPosition(btv, dragEvent);
                    }
                    return true;
            }
            return false;
@@ -382,6 +408,93 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
        maybeOnDragEnd();
    }

    private void animateGlobalDragViewToOriginalPosition(BubbleTextView btv,
            DragEvent dragEvent) {
        SurfaceControl dragSurface = dragEvent.getDragSurface();

        // For top level icons, the target is the icon itself
        View target = btv;
        Object tag = btv.getTag();
        if (tag instanceof ItemInfo) {
            ItemInfo item = (ItemInfo) tag;
            TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
            if (item.container == CONTAINER_ALL_APPS) {
                // Since all apps closes when the drag starts, target the all apps button instead
                target = taskbarViewController.getAllAppsButtonView();
            } else if (item.container >= 0) {
                // Since folders close when the drag starts, target the folder icon instead
                LauncherBindableItemsContainer.ItemOperator op = (info, v) -> {
                    if (info instanceof FolderInfo && v instanceof FolderIcon) {
                        FolderInfo fi = (FolderInfo) info;
                        for (WorkspaceItemInfo si : fi.contents) {
                            if (si.id == item.id) {
                                // Found the parent
                                return true;
                            }
                        }
                    }
                    return false;
                };
                target = taskbarViewController.mapOverItems(op);
            }
        }

        // Finish any pending return animation before starting a new drag
        if (mReturnAnimator != null) {
            mReturnAnimator.end();
        }

        float fromX = dragEvent.getX() - dragEvent.getOffsetX();
        float fromY = dragEvent.getY() - dragEvent.getOffsetY();
        int[] toPosition = target.getLocationOnScreen();
        float toScale = (float) target.getWidth() / mDragIconSize;
        float toAlpha = (target == btv) ? 1f : 0f;
        final ViewRootImpl viewRoot = target.getViewRootImpl();
        SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
        mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
        mReturnAnimator.setDuration(300);
        mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
        mReturnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float t = animation.getAnimatedFraction();
                float accelT = Interpolators.ACCEL_2.getInterpolation(t);
                float scale = 1f - t * (1f - toScale);
                float alpha = 1f - accelT * (1f - toAlpha);
                tx.setPosition(dragSurface, Utilities.mapRange(t, fromX, toPosition[0]),
                        Utilities.mapRange(t, fromY, toPosition[1]));
                tx.setScale(dragSurface, scale, scale);
                tx.setAlpha(dragSurface, alpha);
                tx.apply();
            }
        });
        mReturnAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationCancel(Animator animation) {
                cleanUpSurface();
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                cleanUpSurface();
            }

            private void cleanUpSurface() {
                maybeOnDragEnd();
                // Synchronize removing the drag surface with the next draw after calling
                // maybeOnDragEnd()
                viewRoot.consumeNextDraw((transaction) -> {
                    transaction.remove(dragSurface);
                    transaction.apply();
                    tx.close();
                });
                viewRoot.getView().invalidate();
                mReturnAnimator = null;
            }
        });
        mReturnAnimator.start();
    }

    @Override
    protected float getX(MotionEvent ev) {
        // We will resize to fill the screen while dragging, so use screen coordinates. This ensures
+14 −2
Original line number Diff line number Diff line
@@ -316,6 +316,13 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        return icons;
    }

    /**
     * Returns the all apps button in the taskbar.
     */
    public View getAllAppsButtonView() {
        return mAllAppsButton;
    }

    // FolderIconParent implemented methods.

    @Override
@@ -357,13 +364,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        return getVisibility() == VISIBLE;
    }

    protected void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
    /**
     * Maps {@code op} over all the child views, returning the view that {@code op} evaluates
     * {@code true} for, or {@code null} if none satisfy {@code op}.
     */
    protected View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
        // map over all the shortcuts on the taskbar
        for (int i = 0; i < getChildCount(); i++) {
            View item = getChildAt(i);
            if (op.evaluate((ItemInfo) item.getTag(), item)) {
                return;
                return item;
            }
        }
        return null;
    }
}
+6 −2
Original line number Diff line number Diff line
@@ -164,6 +164,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
        return mTaskbarView.getIconViews();
    }

    public View getAllAppsButtonView() {
        return mTaskbarView.getAllAppsButtonView();
    }

    public AnimatedFloat getTaskbarIconScaleForStash() {
        return mTaskbarIconScaleForStash;
    }
@@ -268,8 +272,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
        mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
    }

    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
        mTaskbarView.mapOverItems(op);
    public View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
        return mTaskbarView.mapOverItems(op);
    }

    /**