Fix launch animation issue for Desktop tile
- See http://go/b320307512_problems for details
- Adding the following to DesktopTaskView's launch animation
- Problem 1: Animate away DesktopTaskView's background according to fullscreenProgress, instead of a jump cut
- Problem 2: Animate Overview's background to transparent to reveal the wallpaper behind
- Problem 3: Animate wallpaper's zoom back to 1
- Problem 4: Apply TaskRectTransltion in TaskViewSimualtor for DesktopTaksView as well, which is used to account for TaskView not in center.
- Problem 4: Also applied RecentsView zoom in animation when TaskView is not in center, to make the whole DesktopTaskView container (backgroundView) moves as a whole. This requires calculating a new pivot before applying the zoom.
- Problem 5: When swipe down or quick switch from home into Desktop, run DesktopRecentsTransitionController without animation to put everything in the end state immediately to avoid animating again
- Problem 6: For swipe down animation, we now let createAdjacentPageAnimForTaskLaunch handle the wallpaper zoom and splash alpha, instead of doubling the effort. In case of Desktop, wallpaper depth be aniamted to 0 (no zoom/blur)
- Problem 6: Bring Desktop live tile to front during swipe down to avoid wallpaper blur applying to the desktop
- Problem 7 and 8: Crop out desktop window outside full screen bound. This is done by calulating intersection between full screen Rect and desktop window's Rect
- Problem 8: Make swipe down to use thumbnailBounds instead of bounds of the first snapshotView, this fixes all situations that first snapshotView's height is different from TaskView (e.g. vertical split, DesktopTaskView)
- Problem 9: Desktop remote target's alpha should remain 1
Bug: 320307512
Test: TaplTestsOverviewDesktop, motion fixes are tested manually
Flag: com.android.window.flags.enable_desktop_windowing_mode
Change-Id: Iae6e594424dfc89851b4f7179b0fb5b5ac5e7f46
Signed-off-by: Alex Chau <alexchau@google.com>
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 9178062..189deda 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -44,11 +44,13 @@
/** Launch desktop tasks from recents view */
fun launchDesktopFromRecents(
desktopTaskView: DesktopTaskView,
+ animated: Boolean,
callback: Consumer<Boolean>? = null
) {
val animRunner =
RemoteDesktopLaunchTransitionRunner(
desktopTaskView,
+ animated,
stateManager,
depthController,
callback
@@ -64,6 +66,7 @@
private class RemoteDesktopLaunchTransitionRunner(
private val desktopTaskView: DesktopTaskView,
+ private val animated: Boolean,
private val stateManager: StateManager<*, *>,
private val depthController: DepthController?,
private val successCallback: Consumer<Boolean>?
@@ -84,16 +87,21 @@
}
MAIN_EXECUTOR.execute {
- TaskViewUtils.composeRecentsDesktopLaunchAnimator(
- desktopTaskView,
- stateManager,
- depthController,
- info,
- t
- ) {
- errorHandlingFinishCallback.run()
- successCallback?.accept(true)
+ val animator =
+ TaskViewUtils.composeRecentsDesktopLaunchAnimator(
+ desktopTaskView,
+ stateManager,
+ depthController,
+ info,
+ t
+ ) {
+ errorHandlingFinishCallback.run()
+ successCallback?.accept(true)
+ }
+ if (!animated) {
+ animator.setDuration(0)
}
+ animator.start()
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 3c7f335..eafc5b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -22,9 +22,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.graphics.Rect;
import android.os.VibrationEffect;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
@@ -67,7 +67,7 @@
protected final CONTAINER mContainer;
private final SingleAxisSwipeDetector mDetector;
private final RecentsView mRecentsView;
- private final int[] mTempCords = new int[2];
+ private final Rect mTempRect = new Rect();
private final boolean mIsRtl;
private AnimatorPlaybackController mCurrentAnimation;
@@ -252,10 +252,8 @@
mTaskBeingDragged, maxDuration, currentInterpolator);
// Since the thumbnail is what is filling the screen, based the end displacement on it.
- View thumbnailView = mTaskBeingDragged.getFirstSnapshotView();
- mTempCords[1] = orientationHandler.getSecondaryDimension(thumbnailView);
- dl.getDescendantCoordRelativeToSelf(thumbnailView, mTempCords);
- mEndDisplacement = secondaryLayerDimension - mTempCords[1];
+ mTaskBeingDragged.getThumbnailBounds(mTempRect, /*relativeToDragLayer=*/true);
+ mEndDisplacement = secondaryLayerDimension - mTempRect.bottom;
}
mEndDisplacement *= verticalFactor;
mCurrentAnimation = pa.createPlaybackController();
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index bd44283..1a09691 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -232,9 +232,13 @@
TaskViewSimulator::setTaskRectTranslation, taskRectTranslationPrimary,
taskRectTranslationSecondary);
- // Fade in the task during the initial 20% of the animation
- out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1,
- clampToProgress(LINEAR, 0, 0.2f));
+ if (v instanceof DesktopTaskView) {
+ targetHandle.getTransformParams().setTargetAlpha(1f);
+ } else {
+ // Fade in the task during the initial 20% of the animation
+ out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0,
+ 1, clampToProgress(LINEAR, 0, 0.2f));
+ }
}
}
@@ -558,7 +562,7 @@
/**
* Start recents to desktop animation
*/
- public static void composeRecentsDesktopLaunchAnimator(
+ public static AnimatorSet composeRecentsDesktopLaunchAnimator(
@NonNull DesktopTaskView launchingTaskView,
@NonNull StateManager stateManager, @Nullable DepthController depthController,
@NonNull TransitionInfo transitionInfo,
@@ -588,7 +592,7 @@
true /* launcherClosing */, stateManager, launchingTaskView.getRecentsView(),
depthController);
- animatorSet.start();
+ return animatorSet;
}
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index d9b7d20..e5c54bb 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -171,7 +171,6 @@
mTaskRect.set(mFullTaskSize);
mOrientationState.getOrientationHandler()
.setSplitTaskSwipeRect(mDp, mTaskRect, mSplitBounds, mStagePosition);
- mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
} else if (mIsDesktopTask) {
// For desktop, tasks can take up only part of the screen size.
// Full task size represents the whole screen size, but scaled down to fit in recents.
@@ -185,10 +184,19 @@
mTaskRect.scale(scale);
// Ensure the task rect is inside the full task rect
mTaskRect.offset(mFullTaskSize.left, mFullTaskSize.top);
+
+ Rect taskDimension = new Rect(0, 0, (int) fullscreenTaskDimension.x,
+ (int) fullscreenTaskDimension.y);
+ mTmpCropRect.set(mThumbnailPosition);
+ if (mTmpCropRect.setIntersect(taskDimension, mThumbnailPosition)) {
+ mTmpCropRect.offset(-mThumbnailPosition.left, -mThumbnailPosition.top);
+ } else {
+ mTmpCropRect.setEmpty();
+ }
} else {
mTaskRect.set(mFullTaskSize);
- mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
}
+ mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
}
/**
@@ -486,10 +494,12 @@
recentsViewPrimaryTranslation.value);
applyWindowToHomeRotation(mMatrix);
- // Crop rect is the inverse of thumbnail matrix
- mTempRectF.set(0, 0, taskWidth, taskHeight);
- mInversePositionMatrix.mapRect(mTempRectF);
- mTempRectF.roundOut(mTmpCropRect);
+ if (!mIsDesktopTask) {
+ // Crop rect is the inverse of thumbnail matrix
+ mTempRectF.set(0, 0, taskWidth, taskHeight);
+ mInversePositionMatrix.mapRect(mTempRectF);
+ mTempRectF.roundOut(mTmpCropRect);
+ }
params.setProgress(1f - fullScreenProgress);
params.applySurfaceParams(surfaceTransaction == null
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index aa628f8..1f58425 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -242,7 +242,7 @@
}
}
- override fun launchTaskAnimated(): RunnableList? {
+ private fun launchTaskWithDesktopController(animated: Boolean): RunnableList? {
val recentsView = recentsView ?: return null
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN,
@@ -252,17 +252,23 @@
val endCallback = RunnableList()
val desktopController = recentsView.desktopRecentsController
checkNotNull(desktopController) { "recentsController is null" }
- desktopController.launchDesktopFromRecents(this) { endCallback.executeAllAndDestroy() }
- Log.d(TAG, "launchTaskAnimated - launchDesktopFromRecents: ${taskIds.contentToString()}")
+ desktopController.launchDesktopFromRecents(this, animated) {
+ endCallback.executeAllAndDestroy()
+ }
+ Log.d(
+ TAG,
+ "launchTaskAnimated - launchTaskWithDesktopController: ${taskIds.contentToString()}, withRemoteTransition: $animated"
+ )
// Callbacks get run from recentsView for case when recents animation already running
recentsView.addSideTaskLaunchCallback(endCallback)
return endCallback
}
+ override fun launchTaskAnimated() = launchTaskWithDesktopController(animated = true)
+
override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
- launchTasks()
- callback(true)
+ launchTaskWithDesktopController(animated = false)?.add { callback(true) } ?: callback(false)
}
// Desktop tile can't be in split screen
@@ -272,8 +278,7 @@
override fun setOverlayEnabled(overlayEnabled: Boolean) {}
override fun onFullscreenProgressChanged(fullscreenProgress: Float) {
- // Don't show background while we are transitioning to/from fullscreen
- backgroundView.visibility = if (fullscreenProgress > 0) INVISIBLE else VISIBLE
+ backgroundView.alpha = 1 - fullscreenProgress
}
override fun updateCurrentFullscreenParams() {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5a6c278..53e6e72 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -40,6 +40,7 @@
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
@@ -57,6 +58,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.quickstep.BaseContainerInterface.getTaskDimension;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_DOWN;
@@ -90,6 +92,7 @@
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
@@ -5232,23 +5235,37 @@
* to the right.
*/
@SuppressLint("Recycle")
- public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
+ public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView taskView) {
AnimatorSet anim = new AnimatorSet();
- int taskIndex = indexOfChild(tv);
+ int taskIndex = indexOfChild(taskView);
int centerTaskIndex = getCurrentPage();
float toScale = getMaxScaleForFullScreen();
boolean showAsGrid = showAsGrid();
- boolean launchingCenterTask = showAsGrid
- ? tv.isFocusedTask() && isTaskViewFullyVisible(tv)
+ boolean zoomInTaskView = showAsGrid
+ ? ((taskView.isFocusedTask() && isTaskViewFullyVisible(taskView))
+ || taskView instanceof DesktopTaskView)
: taskIndex == centerTaskIndex;
- if (launchingCenterTask) {
+ if (zoomInTaskView) {
anim.play(ObjectAnimator.ofFloat(this, RECENTS_SCALE_PROPERTY, toScale));
anim.play(ObjectAnimator.ofFloat(this, FULLSCREEN_PROGRESS, 1));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
+ taskView.getThumbnailBounds(mTempRect, /*relativeToDragLayer=*/true);
+ getTaskDimension(mContext, mContainer.getDeviceProfile(), mTempPointF);
+ Rect fullscreenBounds = new Rect(0, 0, (int) mTempPointF.x,
+ (int) mTempPointF.y);
+ Utilities.getPivotsForScalingRectToRect(mTempRect, fullscreenBounds,
+ mTempPointF);
+ setPivotX(mTempPointF.x);
+ setPivotY(mTempPointF.y);
+ }
+ });
} else if (!showAsGrid) {
// We are launching an adjacent task, so parallax the center and other adjacent task.
- float displacementX = tv.getWidth() * (toScale - 1f);
+ float displacementX = taskView.getWidth() * (toScale - 1f);
float primaryTranslation = mIsRtl ? -displacementX : displacementX;
anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
getPagedOrientationHandler().getPrimaryViewTranslate(), primaryTranslation));
@@ -5276,6 +5293,17 @@
}
}
anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0, 1));
+ if (taskView instanceof DesktopTaskView) {
+ anim.play(ObjectAnimator.ofArgb(mContainer.getScrimView(), VIEW_BACKGROUND_COLOR,
+ Color.TRANSPARENT));
+ }
+ DepthController depthController = getDepthController();
+ if (depthController != null) {
+ float targetDepth = taskView instanceof DesktopTaskView ? 0 : BACKGROUND_APP.getDepth(
+ mContainer);
+ anim.play(ObjectAnimator.ofFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE,
+ targetDepth));
+ }
return anim;
}
@@ -5301,7 +5329,7 @@
}
public PendingAnimation createTaskLaunchAnimation(
- TaskView tv, long duration, Interpolator interpolator) {
+ TaskView taskView, long duration, Interpolator interpolator) {
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
@@ -5316,13 +5344,13 @@
updateGridProperties();
updateScrollSynchronously();
- int targetSysUiFlags = tv.getTaskContainers().getFirst().getSysUiStatusNavFlags();
+ int targetSysUiFlags = taskView.getTaskContainers().getFirst().getSysUiStatusNavFlags();
final boolean[] passedOverviewThreshold = new boolean[]{false};
- ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
- progressAnim.addUpdateListener(animator -> {
+ AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(taskView);
+ anim.play(new AnimatedFloat(v -> {
// Once we pass a certain threshold, update the sysui flags to match the target
// tasks' flags
- if (animator.getAnimatedFraction() > UPDATE_SYSUI_FLAGS_THRESHOLD) {
+ if (v > UPDATE_SYSUI_FLAGS_THRESHOLD) {
mContainer.getSystemUiController().updateUiState(
UI_STATE_FULLSCREEN_TASK, targetSysUiFlags);
} else {
@@ -5330,8 +5358,7 @@
}
// Passing the threshold from taskview to fullscreen app will vibrate
- final boolean passed = animator.getAnimatedFraction() >=
- SUCCESS_TRANSITION_PROGRESS;
+ final boolean passed = v >= SUCCESS_TRANSITION_PROGRESS;
if (passed != passedOverviewThreshold[0]) {
passedOverviewThreshold[0] = passed;
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
@@ -5341,19 +5368,7 @@
mRecentsAnimationController.setWillFinishToHome(!passed);
}
}
- });
-
- AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv);
-
- DepthController depthController = getDepthController();
- if (depthController != null) {
- ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController.stateDepth,
- MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mContainer));
- anim.play(depthAnimator);
- }
- anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0f, 1f));
-
- anim.play(progressAnim);
+ }).animateToValue(0f, 1f));
anim.setInterpolator(interpolator);
mPendingAnimation = new PendingAnimation(duration);
@@ -5362,9 +5377,18 @@
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.addOverviewToAppAnim(mPendingAnimation, interpolator));
mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
+ if (taskView instanceof DesktopTaskView && mRemoteTargetHandles != null) {
+ mPendingAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
+ }
+ });
+ }
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
- if (tv instanceof GroupedTaskView && hasAllValidTaskIds(tv.getTaskIds())
+ if (taskView instanceof GroupedTaskView && hasAllValidTaskIds(taskView.getTaskIds())
&& mRemoteTargetHandles != null) {
// TODO(b/194414938): make this part of the animations instead.
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
@@ -5374,13 +5398,13 @@
dividerAnimator.end();
});
}
- if (tv.isRunningTask()) {
+ if (taskView.isRunningTask()) {
finishRecentsAnimation(false /* toRecents */, null);
onTaskLaunchAnimationEnd(true /* success */);
} else {
- tv.launchTask(this::onTaskLaunchAnimationEnd);
+ taskView.launchTask(this::onTaskLaunchAnimationEnd);
}
- mContainer.getStatsLogManager().logger().withItemInfo(tv.getFirstItemInfo())
+ mContainer.getStatsLogManager().logger().withItemInfo(taskView.getFirstItemInfo())
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
} else {
onTaskLaunchAnimationEnd(false);