Merge "Intruduce Cross Activity Animator"
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.wm.shell.animation;
|
||||
|
||||
import android.graphics.Path;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
@@ -52,6 +53,11 @@ public class Interpolators {
|
||||
*/
|
||||
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
|
||||
|
||||
/**
|
||||
* The default emphasized interpolator. Used for hero / emphasized movement of content.
|
||||
*/
|
||||
public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
|
||||
|
||||
/**
|
||||
* The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
|
||||
* is disappearing e.g. when moving off screen.
|
||||
@@ -81,4 +87,14 @@ public class Interpolators {
|
||||
|
||||
public static final PathInterpolator DIM_INTERPOLATOR =
|
||||
new PathInterpolator(.23f, .87f, .52f, -0.11f);
|
||||
|
||||
// Create the default emphasized interpolator
|
||||
private static PathInterpolator createEmphasizedInterpolator() {
|
||||
Path path = new Path();
|
||||
// Doing the same as fast_out_extra_slow_in
|
||||
path.moveTo(0f, 0f);
|
||||
path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
|
||||
path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
|
||||
return new PathInterpolator(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.back;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Color;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||
|
||||
/**
|
||||
* Controls background surface for the back animations
|
||||
*/
|
||||
public class BackAnimationBackground {
|
||||
private static final int BACKGROUND_LAYER = -1;
|
||||
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
|
||||
private SurfaceControl mBackgroundSurface;
|
||||
|
||||
public BackAnimationBackground(RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
|
||||
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
|
||||
}
|
||||
|
||||
void ensureBackground(int color, @NonNull SurfaceControl.Transaction transaction) {
|
||||
if (mBackgroundSurface != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float[] colorComponents = new float[] { Color.red(color) / 255.f,
|
||||
Color.green(color) / 255.f, Color.blue(color) / 255.f };
|
||||
|
||||
final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
|
||||
.setName("back-animation-background")
|
||||
.setCallsite("BackAnimationBackground")
|
||||
.setColorLayer();
|
||||
|
||||
mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
|
||||
mBackgroundSurface = colorLayerBuilder.build();
|
||||
transaction.setColor(mBackgroundSurface, colorComponents)
|
||||
.setLayer(mBackgroundSurface, BACKGROUND_LAYER)
|
||||
.show(mBackgroundSurface);
|
||||
}
|
||||
|
||||
void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
|
||||
if (mBackgroundSurface == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBackgroundSurface.isValid()) {
|
||||
transaction.remove(mBackgroundSurface);
|
||||
}
|
||||
mBackgroundSurface = null;
|
||||
}
|
||||
}
|
||||
@@ -138,14 +138,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
};
|
||||
|
||||
private final BackAnimationBackground mAnimationBackground;
|
||||
|
||||
public BackAnimationController(
|
||||
@NonNull ShellInit shellInit,
|
||||
@NonNull ShellController shellController,
|
||||
@NonNull @ShellMainThread ShellExecutor shellExecutor,
|
||||
@NonNull @ShellBackgroundThread Handler backgroundHandler,
|
||||
Context context) {
|
||||
Context context,
|
||||
@NonNull BackAnimationBackground backAnimationBackground) {
|
||||
this(shellInit, shellController, shellExecutor, backgroundHandler,
|
||||
ActivityTaskManager.getService(), context, context.getContentResolver());
|
||||
ActivityTaskManager.getService(), context, context.getContentResolver(),
|
||||
backAnimationBackground);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -155,7 +159,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
@NonNull @ShellMainThread ShellExecutor shellExecutor,
|
||||
@NonNull @ShellBackgroundThread Handler bgHandler,
|
||||
@NonNull IActivityTaskManager activityTaskManager,
|
||||
Context context, ContentResolver contentResolver) {
|
||||
Context context, ContentResolver contentResolver,
|
||||
@NonNull BackAnimationBackground backAnimationBackground) {
|
||||
mShellController = shellController;
|
||||
mShellExecutor = shellExecutor;
|
||||
mActivityTaskManager = activityTaskManager;
|
||||
@@ -163,6 +168,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
mContentResolver = contentResolver;
|
||||
mBgHandler = bgHandler;
|
||||
shellInit.addInitCallback(this::onInit, this);
|
||||
mAnimationBackground = backAnimationBackground;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -184,10 +190,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
return;
|
||||
}
|
||||
|
||||
final CrossTaskBackAnimation crossTaskAnimation = new CrossTaskBackAnimation(mContext);
|
||||
final CrossTaskBackAnimation crossTaskAnimation =
|
||||
new CrossTaskBackAnimation(mContext, mAnimationBackground);
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_TASK,
|
||||
new BackAnimationRunner(crossTaskAnimation.mCallback, crossTaskAnimation.mRunner));
|
||||
// TODO (238474994): register cross activity animation when it's completed.
|
||||
crossTaskAnimation.mBackAnimationRunner);
|
||||
final CrossActivityAnimation crossActivityAnimation =
|
||||
new CrossActivityAnimation(mContext, mAnimationBackground);
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
|
||||
crossActivityAnimation.mBackAnimationRunner);
|
||||
// TODO (236760237): register dialog close animation when it's completed.
|
||||
}
|
||||
|
||||
@@ -275,7 +285,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
@Override
|
||||
public void clearBackToLauncherCallback() {
|
||||
executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback",
|
||||
(controller) -> controller.clearBackToLauncherCallback());
|
||||
(controller) -> controller.unregisterAnimation(
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -289,8 +300,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
mAnimationDefinition.set(type, runner);
|
||||
}
|
||||
|
||||
private void clearBackToLauncherCallback() {
|
||||
mAnimationDefinition.remove(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
|
||||
mAnimationDefinition.remove(type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.back;
|
||||
|
||||
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
|
||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
|
||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.window.BackEvent;
|
||||
import android.window.BackProgressAnimator;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
import com.android.internal.policy.ScreenDecorationsUtils;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
|
||||
/** Class that defines cross-activity animation. */
|
||||
@ShellMainThread
|
||||
class CrossActivityAnimation {
|
||||
/**
|
||||
* Minimum scale of the entering/closing window.
|
||||
*/
|
||||
private static final float MIN_WINDOW_SCALE = 0.9f;
|
||||
|
||||
/**
|
||||
* Minimum alpha of the closing/entering window.
|
||||
*/
|
||||
private static final float CLOSING_MIN_WINDOW_ALPHA = 0.5f;
|
||||
|
||||
/**
|
||||
* Progress value to fly out closing window and fly in entering window.
|
||||
*/
|
||||
private static final float SWITCH_ENTERING_WINDOW_PROGRESS = 0.5f;
|
||||
|
||||
/** Max window translation in the Y axis. */
|
||||
private static final int WINDOW_MAX_DELTA_Y = 160;
|
||||
|
||||
/** Duration of fade in/out entering window. */
|
||||
private static final int FADE_IN_DURATION = 100;
|
||||
/** Duration of post animation after gesture committed. */
|
||||
private static final int POST_ANIMATION_DURATION = 350;
|
||||
private static final Interpolator INTERPOLATOR = Interpolators.EMPHASIZED;
|
||||
|
||||
private final Rect mStartTaskRect = new Rect();
|
||||
private final float mCornerRadius;
|
||||
|
||||
// The closing window properties.
|
||||
private final RectF mClosingRect = new RectF();
|
||||
|
||||
// The entering window properties.
|
||||
private final Rect mEnteringStartRect = new Rect();
|
||||
private final RectF mEnteringRect = new RectF();
|
||||
|
||||
private float mCurrentAlpha = 1.0f;
|
||||
|
||||
private float mEnteringMargin = 0;
|
||||
private ValueAnimator mEnteringAnimator;
|
||||
private boolean mEnteringWindowShow = false;
|
||||
|
||||
private final PointF mInitialTouchPos = new PointF();
|
||||
|
||||
private final Matrix mTransformMatrix = new Matrix();
|
||||
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
private RemoteAnimationTarget mEnteringTarget;
|
||||
private RemoteAnimationTarget mClosingTarget;
|
||||
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
|
||||
private boolean mBackInProgress = false;
|
||||
|
||||
private PointF mTouchPos = new PointF();
|
||||
private IRemoteAnimationFinishedCallback mFinishCallback;
|
||||
|
||||
private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
|
||||
final BackAnimationRunner mBackAnimationRunner;
|
||||
|
||||
private final BackAnimationBackground mBackground;
|
||||
|
||||
CrossActivityAnimation(Context context, BackAnimationBackground background) {
|
||||
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
|
||||
mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner());
|
||||
mBackground = background;
|
||||
}
|
||||
|
||||
private static float mapRange(float value, float min, float max) {
|
||||
return min + (value * (max - min));
|
||||
}
|
||||
|
||||
private float getInterpolatedProgress(float backProgress) {
|
||||
return INTERPOLATOR.getInterpolation(backProgress);
|
||||
}
|
||||
|
||||
private void startBackAnimation() {
|
||||
if (mEnteringTarget == null || mClosingTarget == null) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null.");
|
||||
return;
|
||||
}
|
||||
mTransaction.setAnimationTransaction();
|
||||
|
||||
// Offset start rectangle to align task bounds.
|
||||
mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds());
|
||||
mStartTaskRect.offsetTo(0, 0);
|
||||
|
||||
// Draw background with task background color.
|
||||
mBackground.ensureBackground(
|
||||
mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction);
|
||||
}
|
||||
|
||||
private void applyTransform(SurfaceControl leash, RectF targetRect, float targetAlpha) {
|
||||
final float scale = targetRect.width() / mStartTaskRect.width();
|
||||
mTransformMatrix.reset();
|
||||
mTransformMatrix.setScale(scale, scale);
|
||||
mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
|
||||
mTransaction.setAlpha(leash, targetAlpha)
|
||||
.setMatrix(leash, mTransformMatrix, mTmpFloat9)
|
||||
.setWindowCrop(leash, mStartTaskRect)
|
||||
.setCornerRadius(leash, mCornerRadius);
|
||||
}
|
||||
|
||||
private void finishAnimation() {
|
||||
if (mEnteringTarget != null) {
|
||||
mEnteringTarget.leash.release();
|
||||
mEnteringTarget = null;
|
||||
}
|
||||
if (mClosingTarget != null) {
|
||||
mClosingTarget.leash.release();
|
||||
mClosingTarget = null;
|
||||
}
|
||||
if (mBackground != null) {
|
||||
mBackground.removeBackground(mTransaction);
|
||||
}
|
||||
|
||||
mTransaction.apply();
|
||||
mBackInProgress = false;
|
||||
mTransformMatrix.reset();
|
||||
mInitialTouchPos.set(0, 0);
|
||||
mEnteringWindowShow = false;
|
||||
mEnteringMargin = 0;
|
||||
|
||||
if (mFinishCallback != null) {
|
||||
try {
|
||||
mFinishCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mFinishCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void onGestureProgress(@NonNull BackEvent backEvent) {
|
||||
if (!mBackInProgress) {
|
||||
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
|
||||
mBackInProgress = true;
|
||||
}
|
||||
mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
|
||||
|
||||
if (mEnteringTarget == null || mClosingTarget == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float progress = getInterpolatedProgress(backEvent.getProgress());
|
||||
final float touchY = mTouchPos.y;
|
||||
|
||||
final int width = mStartTaskRect.width();
|
||||
final int height = mStartTaskRect.height();
|
||||
|
||||
final float closingScale = mapRange(progress, 1, MIN_WINDOW_SCALE);
|
||||
|
||||
final float closingWidth = closingScale * width;
|
||||
final float closingHeight = (float) height / width * closingWidth;
|
||||
|
||||
// Move the window along the X axis.
|
||||
final float closingLeft = mStartTaskRect.left + (width - closingWidth) / 2;
|
||||
|
||||
// Move the window along the Y axis.
|
||||
final float deltaYRatio = (touchY - mInitialTouchPos.y) / height;
|
||||
final float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * WINDOW_MAX_DELTA_Y;
|
||||
final float closingTop = (height - closingHeight) * 0.5f + deltaY;
|
||||
mClosingRect.set(
|
||||
closingLeft, closingTop, closingLeft + closingWidth, closingTop + closingHeight);
|
||||
mEnteringRect.set(mClosingRect);
|
||||
|
||||
// Switch closing/entering targets while reach to the threshold progress.
|
||||
if (showEnteringWindow(progress > SWITCH_ENTERING_WINDOW_PROGRESS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Present windows and update the alpha.
|
||||
mCurrentAlpha = Math.max(mapRange(progress, 1.0f, 0), CLOSING_MIN_WINDOW_ALPHA);
|
||||
mClosingRect.offset(mEnteringMargin, 0);
|
||||
mEnteringRect.offset(mEnteringMargin - width, 0);
|
||||
|
||||
applyTransform(
|
||||
mClosingTarget.leash, mClosingRect, mEnteringWindowShow ? 0.01f : mCurrentAlpha);
|
||||
applyTransform(
|
||||
mEnteringTarget.leash, mEnteringRect, mEnteringWindowShow ? mCurrentAlpha : 0.01f);
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
private boolean showEnteringWindow(boolean show) {
|
||||
if (mEnteringAnimator == null) {
|
||||
mEnteringAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(FADE_IN_DURATION);
|
||||
mEnteringAnimator.setInterpolator(new AccelerateInterpolator());
|
||||
mEnteringAnimator.addUpdateListener(animation -> {
|
||||
float progress = animation.getAnimatedFraction();
|
||||
final int width = mStartTaskRect.width();
|
||||
mEnteringMargin = width * progress;
|
||||
// We don't animate to 0 or the surface would become invisible and lose focus.
|
||||
final float alpha = progress >= 0.5f ? 0.01f
|
||||
: mapRange(progress * 2, mCurrentAlpha, 0.01f);
|
||||
mClosingRect.offset(mEnteringMargin, 0);
|
||||
mEnteringRect.offset(mEnteringMargin - width, 0);
|
||||
|
||||
applyTransform(mClosingTarget.leash, mClosingRect, alpha);
|
||||
applyTransform(mEnteringTarget.leash, mEnteringRect, mCurrentAlpha);
|
||||
mTransaction.apply();
|
||||
});
|
||||
}
|
||||
|
||||
if (mEnteringAnimator.isRunning()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mEnteringWindowShow == show) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mEnteringWindowShow = show;
|
||||
if (show) {
|
||||
mEnteringAnimator.start();
|
||||
} else {
|
||||
mEnteringAnimator.reverse();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onGestureCommitted() {
|
||||
if (mEnteringTarget == null || mClosingTarget == null) {
|
||||
finishAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
// End the fade in animation.
|
||||
if (mEnteringAnimator.isRunning()) {
|
||||
mEnteringAnimator.cancel();
|
||||
}
|
||||
|
||||
// We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
|
||||
// coordinate of the gesture driven phase.
|
||||
mEnteringRect.round(mEnteringStartRect);
|
||||
mTransaction.hide(mClosingTarget.leash);
|
||||
|
||||
ValueAnimator valueAnimator =
|
||||
ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION);
|
||||
valueAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
valueAnimator.addUpdateListener(animation -> {
|
||||
float progress = animation.getAnimatedFraction();
|
||||
updatePostCommitEnteringAnimation(progress);
|
||||
mTransaction.apply();
|
||||
});
|
||||
|
||||
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
finishAnimation();
|
||||
}
|
||||
});
|
||||
valueAnimator.start();
|
||||
}
|
||||
|
||||
private void updatePostCommitEnteringAnimation(float progress) {
|
||||
float left = mapRange(progress, mEnteringStartRect.left, mStartTaskRect.left);
|
||||
float top = mapRange(progress, mEnteringStartRect.top, mStartTaskRect.top);
|
||||
float width = mapRange(progress, mEnteringStartRect.width(), mStartTaskRect.width());
|
||||
float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height());
|
||||
float alpha = mapRange(progress, mCurrentAlpha, 1.0f);
|
||||
|
||||
mEnteringRect.set(left, top, left + width, top + height);
|
||||
applyTransform(mEnteringTarget.leash, mEnteringRect, alpha);
|
||||
}
|
||||
|
||||
private final class Callback extends IOnBackInvokedCallback.Default {
|
||||
@Override
|
||||
public void onBackStarted(BackEvent backEvent) {
|
||||
mProgressAnimator.onBackStarted(backEvent,
|
||||
CrossActivityAnimation.this::onGestureProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackProgressed(@NonNull BackEvent backEvent) {
|
||||
mProgressAnimator.onBackProgressed(backEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
// End the fade in animation.
|
||||
if (mEnteringAnimator.isRunning()) {
|
||||
mEnteringAnimator.cancel();
|
||||
}
|
||||
// TODO (b259608500): Let BackProgressAnimator could play cancel animation.
|
||||
mProgressAnimator.reset();
|
||||
finishAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackInvoked() {
|
||||
mProgressAnimator.reset();
|
||||
onGestureCommitted();
|
||||
}
|
||||
}
|
||||
|
||||
private final class Runner extends IRemoteAnimationRunner.Default {
|
||||
@Override
|
||||
public void onAnimationStart(
|
||||
int transit,
|
||||
RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers,
|
||||
RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to activity animation.");
|
||||
for (RemoteAnimationTarget a : apps) {
|
||||
if (a.mode == MODE_CLOSING) {
|
||||
mClosingTarget = a;
|
||||
}
|
||||
if (a.mode == MODE_OPENING) {
|
||||
mEnteringTarget = a;
|
||||
}
|
||||
}
|
||||
|
||||
startBackAnimation();
|
||||
mFinishCallback = finishedCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancelled(boolean isKeyguardOccluded) {
|
||||
finishAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
*/
|
||||
@ShellMainThread
|
||||
class CrossTaskBackAnimation {
|
||||
private static final float[] BACKGROUNDCOLOR = {0.263f, 0.263f, 0.227f};
|
||||
private static final int BACKGROUNDCOLOR = 0x43433A;
|
||||
|
||||
/**
|
||||
* Minimum scale of the entering window.
|
||||
@@ -106,7 +106,6 @@ class CrossTaskBackAnimation {
|
||||
|
||||
private RemoteAnimationTarget mEnteringTarget;
|
||||
private RemoteAnimationTarget mClosingTarget;
|
||||
private SurfaceControl mBackgroundSurface;
|
||||
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
|
||||
private boolean mBackInProgress = false;
|
||||
@@ -115,56 +114,15 @@ class CrossTaskBackAnimation {
|
||||
private float mProgress = 0;
|
||||
private PointF mTouchPos = new PointF();
|
||||
private IRemoteAnimationFinishedCallback mFinishCallback;
|
||||
|
||||
private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
|
||||
final BackAnimationRunner mBackAnimationRunner;
|
||||
|
||||
final IOnBackInvokedCallback mCallback = new IOnBackInvokedCallback.Default() {
|
||||
@Override
|
||||
public void onBackStarted(BackEvent backEvent) {
|
||||
mProgressAnimator.onBackStarted(backEvent,
|
||||
CrossTaskBackAnimation.this::onGestureProgress);
|
||||
}
|
||||
private final BackAnimationBackground mBackground;
|
||||
|
||||
@Override
|
||||
public void onBackProgressed(@NonNull BackEvent backEvent) {
|
||||
mProgressAnimator.onBackProgressed(backEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
mProgressAnimator.reset();
|
||||
finishAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackInvoked() {
|
||||
mProgressAnimator.reset();
|
||||
onGestureCommitted();
|
||||
}
|
||||
};
|
||||
|
||||
final IRemoteAnimationRunner mRunner = new IRemoteAnimationRunner.Default() {
|
||||
@Override
|
||||
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to task animation.");
|
||||
for (RemoteAnimationTarget a : apps) {
|
||||
if (a.mode == MODE_CLOSING) {
|
||||
mClosingTarget = a;
|
||||
}
|
||||
if (a.mode == MODE_OPENING) {
|
||||
mEnteringTarget = a;
|
||||
}
|
||||
}
|
||||
|
||||
startBackAnimation();
|
||||
mFinishCallback = finishedCallback;
|
||||
}
|
||||
};
|
||||
|
||||
CrossTaskBackAnimation(Context context) {
|
||||
CrossTaskBackAnimation(Context context, BackAnimationBackground background) {
|
||||
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
|
||||
mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner());
|
||||
mBackground = background;
|
||||
}
|
||||
|
||||
private float getInterpolatedProgress(float backProgress) {
|
||||
@@ -182,14 +140,7 @@ class CrossTaskBackAnimation {
|
||||
mStartTaskRect.offsetTo(0, 0);
|
||||
|
||||
// Draw background.
|
||||
mBackgroundSurface = new SurfaceControl.Builder()
|
||||
.setName("Background of Back Navigation")
|
||||
.setColorLayer()
|
||||
.setHidden(false)
|
||||
.build();
|
||||
mTransaction.setColor(mBackgroundSurface, BACKGROUNDCOLOR)
|
||||
.setLayer(mBackgroundSurface, -1);
|
||||
mTransaction.apply();
|
||||
mBackground.ensureBackground(BACKGROUNDCOLOR, mTransaction);
|
||||
}
|
||||
|
||||
private void updateGestureBackProgress(float progress, BackEvent event) {
|
||||
@@ -300,11 +251,11 @@ class CrossTaskBackAnimation {
|
||||
mClosingTarget = null;
|
||||
}
|
||||
|
||||
if (mBackgroundSurface != null) {
|
||||
mBackgroundSurface.release();
|
||||
mBackgroundSurface = null;
|
||||
if (mBackground != null) {
|
||||
mBackground.removeBackground(mTransaction);
|
||||
}
|
||||
|
||||
mTransaction.apply();
|
||||
mBackInProgress = false;
|
||||
mTransformMatrix.reset();
|
||||
mClosingCurrentRect.setEmpty();
|
||||
@@ -362,4 +313,49 @@ class CrossTaskBackAnimation {
|
||||
private static float mapRange(float value, float min, float max) {
|
||||
return min + (value * (max - min));
|
||||
}
|
||||
|
||||
private final class Callback extends IOnBackInvokedCallback.Default {
|
||||
@Override
|
||||
public void onBackStarted(BackEvent backEvent) {
|
||||
mProgressAnimator.onBackStarted(backEvent,
|
||||
CrossTaskBackAnimation.this::onGestureProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackProgressed(@NonNull BackEvent backEvent) {
|
||||
mProgressAnimator.onBackProgressed(backEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
mProgressAnimator.reset();
|
||||
finishAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackInvoked() {
|
||||
mProgressAnimator.reset();
|
||||
onGestureCommitted();
|
||||
}
|
||||
};
|
||||
|
||||
private final class Runner extends IRemoteAnimationRunner.Default {
|
||||
@Override
|
||||
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to task animation.");
|
||||
for (RemoteAnimationTarget a : apps) {
|
||||
if (a.mode == MODE_CLOSING) {
|
||||
mClosingTarget = a;
|
||||
}
|
||||
if (a.mode == MODE_OPENING) {
|
||||
mEnteringTarget = a;
|
||||
}
|
||||
}
|
||||
|
||||
startBackAnimation();
|
||||
mFinishCallback = finishedCallback;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.wm.shell.TaskViewTransitions;
|
||||
import com.android.wm.shell.WindowManagerShellWrapper;
|
||||
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
|
||||
import com.android.wm.shell.back.BackAnimation;
|
||||
import com.android.wm.shell.back.BackAnimationBackground;
|
||||
import com.android.wm.shell.back.BackAnimationController;
|
||||
import com.android.wm.shell.bubbles.BubbleController;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
@@ -93,13 +94,13 @@ import com.android.wm.shell.unfold.UnfoldAnimationController;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import dagger.BindsOptionalOf;
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
|
||||
* accessible from components within the WM subcomponent (can be explicitly exposed to the
|
||||
@@ -253,6 +254,13 @@ public abstract class WMShellBaseModule {
|
||||
return backAnimationController.map(BackAnimationController::getBackAnimationImpl);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static BackAnimationBackground provideBackAnimationBackground(
|
||||
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
|
||||
return new BackAnimationBackground(rootTaskDisplayAreaOrganizer);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<BackAnimationController> provideBackAnimationController(
|
||||
@@ -260,16 +268,18 @@ public abstract class WMShellBaseModule {
|
||||
ShellInit shellInit,
|
||||
ShellController shellController,
|
||||
@ShellMainThread ShellExecutor shellExecutor,
|
||||
@ShellBackgroundThread Handler backgroundHandler
|
||||
@ShellBackgroundThread Handler backgroundHandler,
|
||||
BackAnimationBackground backAnimationBackground
|
||||
) {
|
||||
if (BackAnimationController.IS_ENABLED) {
|
||||
return Optional.of(
|
||||
new BackAnimationController(shellInit, shellController, shellExecutor,
|
||||
backgroundHandler, context));
|
||||
backgroundHandler, context, backAnimationBackground));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bubbles (optional feature)
|
||||
//
|
||||
|
||||
@@ -110,6 +110,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
@Mock
|
||||
private ShellController mShellController;
|
||||
|
||||
@Mock
|
||||
private BackAnimationBackground mAnimationBackground;
|
||||
|
||||
private BackAnimationController mController;
|
||||
private TestableContentResolver mContentResolver;
|
||||
private TestableLooper mTestableLooper;
|
||||
@@ -127,7 +130,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
mController = new BackAnimationController(mShellInit, mShellController,
|
||||
mShellExecutor, new Handler(mTestableLooper.getLooper()),
|
||||
mActivityTaskManager, mContext,
|
||||
mContentResolver);
|
||||
mContentResolver, mAnimationBackground);
|
||||
mController.setEnableUAnimation(true);
|
||||
mShellInit.init();
|
||||
mShellExecutor.flushAll();
|
||||
@@ -239,7 +242,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
mController = new BackAnimationController(shellInit, mShellController,
|
||||
mShellExecutor, new Handler(mTestableLooper.getLooper()),
|
||||
mActivityTaskManager, mContext,
|
||||
mContentResolver);
|
||||
mContentResolver, mAnimationBackground);
|
||||
shellInit.init();
|
||||
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
|
||||
@@ -353,6 +356,10 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
BackNavigationInfo.TYPE_CROSS_ACTIVITY,
|
||||
BackNavigationInfo.TYPE_DIALOG_CLOSE};
|
||||
|
||||
for (int type: testTypes) {
|
||||
unregisterAnimation(type);
|
||||
}
|
||||
|
||||
for (int type: testTypes) {
|
||||
final ResultListener result = new ResultListener();
|
||||
createNavigationInfo(new BackNavigationInfo.Builder()
|
||||
@@ -431,6 +438,10 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
new BackAnimationRunner(mAnimatorCallback, mBackAnimationRunner));
|
||||
}
|
||||
|
||||
private void unregisterAnimation(int type) {
|
||||
mController.unregisterAnimation(type);
|
||||
}
|
||||
|
||||
private static class ResultListener implements RemoteCallback.OnResultListener {
|
||||
boolean mBackNavigationDone = false;
|
||||
boolean mTriggerBack = false;
|
||||
|
||||
@@ -241,6 +241,7 @@ class BackNavigationController {
|
||||
// We have another Activity in the same currentTask to go to
|
||||
backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
|
||||
removedWindowContainer = currentActivity;
|
||||
prevTask = prevActivity.getTask();
|
||||
} else if (currentTask.returnsToHomeRootTask()) {
|
||||
// Our Task should bring back to home
|
||||
removedWindowContainer = currentTask;
|
||||
@@ -608,26 +609,23 @@ class BackNavigationController {
|
||||
// reset leash after animation finished.
|
||||
leashes.add(screenshotSurface);
|
||||
}
|
||||
} else if (prevTask != null) {
|
||||
prevActivity = prevTask.getTopNonFinishingActivity();
|
||||
if (prevActivity != null) {
|
||||
// Make previous task show from behind by marking its top activity as visible
|
||||
// and launch-behind to bump its visibility for the duration of the back gesture.
|
||||
setLaunchBehind(prevActivity);
|
||||
} else if (prevTask != null && prevActivity != null) {
|
||||
// Make previous task show from behind by marking its top activity as visible
|
||||
// and launch-behind to bump its visibility for the duration of the back gesture.
|
||||
setLaunchBehind(prevActivity);
|
||||
|
||||
final SurfaceControl leash = prevActivity.makeAnimationLeash()
|
||||
.setName("BackPreview Leash for " + prevActivity)
|
||||
.setHidden(false)
|
||||
.build();
|
||||
prevActivity.reparentSurfaceControl(startedTransaction, leash);
|
||||
behindAppTarget = createRemoteAnimationTargetLocked(
|
||||
prevTask, leash, MODE_OPENING);
|
||||
final SurfaceControl leash = prevActivity.makeAnimationLeash()
|
||||
.setName("BackPreview Leash for " + prevActivity)
|
||||
.setHidden(false)
|
||||
.build();
|
||||
prevActivity.reparentSurfaceControl(startedTransaction, leash);
|
||||
behindAppTarget = createRemoteAnimationTargetLocked(
|
||||
prevTask, leash, MODE_OPENING);
|
||||
|
||||
// reset leash after animation finished.
|
||||
leashes.add(leash);
|
||||
prevActivity.reparentSurfaceControl(finishedTransaction,
|
||||
prevActivity.getParentSurfaceControl());
|
||||
}
|
||||
// reset leash after animation finished.
|
||||
leashes.add(leash);
|
||||
prevActivity.reparentSurfaceControl(finishedTransaction,
|
||||
prevActivity.getParentSurfaceControl());
|
||||
}
|
||||
|
||||
if (mShowWallpaper) {
|
||||
|
||||
Reference in New Issue
Block a user