Loading libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java +16 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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); } } libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java 0 → 100644 +69 −0 Original line number Diff line number Diff line /* * 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; } } libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +20 −9 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -163,6 +168,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mContentResolver = contentResolver; mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; } @VisibleForTesting Loading @@ -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. } Loading Loading @@ -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 Loading @@ -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); } /** Loading libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java 0 → 100644 +373 −0 Original line number Diff line number Diff line /* * 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(); } } } libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +55 −59 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java +16 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java 0 → 100644 +69 −0 Original line number Diff line number Diff line /* * 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; } }
libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +20 −9 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -163,6 +168,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mContentResolver = contentResolver; mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; } @VisibleForTesting Loading @@ -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. } Loading Loading @@ -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 Loading @@ -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); } /** Loading
libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java 0 → 100644 +373 −0 Original line number Diff line number Diff line /* * 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(); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +55 −59 File changed.Preview size limit exceeded, changes collapsed. Show changes