Merge "Add basic return animation when drag is canceled"
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index e152915..435eae4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -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.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.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 @@
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 @@
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 @@
@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 @@
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 @@
}
}
- private void onSystemDragStarted() {
+ private void onSystemDragStarted(BubbleTextView btv) {
mIsSystemDragInProgress = true;
mActivity.getDragLayer().setOnDragListener((view, dragEvent) -> {
switch (dragEvent.getAction()) {
@@ -356,7 +377,12 @@
return true;
case DragEvent.ACTION_DRAG_ENDED:
mIsSystemDragInProgress = false;
- maybeOnDragEnd();
+ 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 @@
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
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 40c67ff..ade58a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -318,6 +318,13 @@
return icons;
}
+ /**
+ * Returns the all apps button in the taskbar.
+ */
+ public View getAllAppsButtonView() {
+ return mAllAppsButton;
+ }
+
// FolderIconParent implemented methods.
@Override
@@ -359,13 +366,18 @@
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;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 30bdce1..62f1fa5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -164,6 +164,10 @@
return mTaskbarView.getIconViews();
}
+ public View getAllAppsButtonView() {
+ return mTaskbarView.getAllAppsButtonView();
+ }
+
public AnimatedFloat getTaskbarIconScaleForStash() {
return mTaskbarIconScaleForStash;
}
@@ -270,8 +274,8 @@
mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
}
- public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
- mTaskbarView.mapOverItems(op);
+ public View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+ return mTaskbarView.mapOverItems(op);
}
/**