Loading core/java/android/window/BackProgressAnimator.java +50 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,12 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.FloatProperty; import android.util.TimeUtils; import android.view.Choreographer; import com.android.internal.dynamicanimation.animation.DynamicAnimation; import com.android.internal.dynamicanimation.animation.FlingAnimation; import com.android.internal.dynamicanimation.animation.FloatValueHolder; import com.android.internal.dynamicanimation.animation.SpringAnimation; import com.android.internal.dynamicanimation.animation.SpringForce; Loading @@ -40,6 +44,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL * always receive progress values in [0, 1]. */ private static final float SCALE_FACTOR = 100f; private static final float FLING_FRICTION = 8f; private final SpringAnimation mSpring; private ProgressCallback mCallback; private float mProgress = 0; Loading @@ -48,11 +53,17 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL private boolean mBackAnimationInProgress = false; @Nullable private Runnable mBackCancelledFinishRunnable; @Nullable private Runnable mBackInvokedFinishRunnable; private FlingAnimation mBackInvokedFlingAnim; private final DynamicAnimation.OnAnimationEndListener mOnAnimationEndListener = (animation, canceled, value, velocity) -> { invokeBackCancelledRunnable(); if (mBackCancelledFinishRunnable != null) invokeBackCancelledRunnable(); if (mBackInvokedFinishRunnable != null) invokeBackInvokedRunnable(); reset(); }; private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener = (animation, progress, velocity) -> updateProgressValue(progress, velocity); private void setProgress(float progress) { Loading @@ -78,7 +89,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL @Override public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { updateProgressValue(value, velocity); if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity); } Loading Loading @@ -134,6 +145,12 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL // Ensure that last progress value that apps see is 0 updateProgressValue(0, 0); invokeBackCancelledRunnable(); } else if (mBackInvokedFinishRunnable != null) { invokeBackInvokedRunnable(); } if (mBackInvokedFlingAnim != null) { mBackInvokedFlingAnim.cancel(); mBackInvokedFlingAnim = null; } mSpring.animateToFinalPosition(0); if (mSpring.canSkipToEnd()) { Loading @@ -148,6 +165,30 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mProgress = 0; } /** * Animate the back progress animation a bit further with a high friction considering the * current progress and velocity. * * @param finishCallback the callback to be invoked when the final destination is reached */ public void onBackInvoked(@NonNull Runnable finishCallback) { mBackInvokedFinishRunnable = finishCallback; mSpring.animateToFinalPosition(0); mBackInvokedFlingAnim = new FlingAnimation(new FloatValueHolder()) .setStartValue(mProgress) .setFriction(FLING_FRICTION) .setStartVelocity(mVelocity) .setMinValue(0) .setMaxValue(SCALE_FACTOR); mBackInvokedFlingAnim.addUpdateListener(mOnBackInvokedFlingUpdateListener); mBackInvokedFlingAnim.addEndListener(mOnAnimationEndListener); mBackInvokedFlingAnim.start(); // do an animation-frame immediately to prevent idle frame mBackInvokedFlingAnim.doAnimationFrame( Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS); } /** * Animate the back progress animation from current progress to start position. * This should be called when back is cancelled. Loading Loading @@ -196,4 +237,11 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mBackCancelledFinishRunnable = null; } private void invokeBackInvokedRunnable() { mBackInvokedFlingAnim.removeUpdateListener(mOnBackInvokedFlingUpdateListener); mBackInvokedFlingAnim.removeEndListener(mOnAnimationEndListener); mBackInvokedFinishRunnable.run(); mBackInvokedFinishRunnable = null; } } No newline at end of file core/java/android/window/WindowOnBackInvokedDispatcher.java +8 −3 Original line number Diff line number Diff line Loading @@ -452,10 +452,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mHandler.post(() -> { mTouchTracker.reset(); boolean isInProgress = mProgressAnimator.isBackAnimationInProgress(); mProgressAnimator.reset(); // TODO(b/333957271): Re-introduce auto fling progress generation. final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { mProgressAnimator.reset(); Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference."); return; } Loading @@ -463,7 +462,13 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Log.w(TAG, "ProgressAnimator was not in progress, skip onBackInvoked()."); return; } OnBackAnimationCallback animationCallback = getBackAnimationCallback(); if (animationCallback != null) { mProgressAnimator.onBackInvoked(callback::onBackInvoked); } else { mProgressAnimator.reset(); callback.onBackInvoked(); } }); } Loading core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java 0 → 100644 +203 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.internal.dynamicanimation.animation; import android.annotation.FloatRange; /** * <p>Fling animation is an animation that continues an initial momentum (most often from gesture * velocity) and gradually slows down. The fling animation will come to a stop when the velocity of * the animation is below the threshold derived from {@link #setMinimumVisibleChange(float)}, * or when the value of the animation has gone beyond the min or max value defined via * {@link DynamicAnimation#setMinValue(float)} or {@link DynamicAnimation#setMaxValue(float)}. * It is recommended to restrict the fling animation with min and/or max value, such that the * animation can end when it goes beyond screen bounds, thus preserving CPU cycles and resources. * * <p>For example, you can create a fling animation that animates the translationX of a view: * <pre class="prettyprint"> * FlingAnimation flingAnim = new FlingAnimation(view, DynamicAnimation.TRANSLATION_X) * // Sets the start velocity to -2000 (pixel/s) * .setStartVelocity(-2000) * // Optional but recommended to set a reasonable min and max range for the animation. * // In this particular case, we set the min and max to -200 and 2000 respectively. * .setMinValue(-200).setMaxValue(2000); * flingAnim.start(); * </pre> */ public final class FlingAnimation extends DynamicAnimation<FlingAnimation> { private final DragForce mFlingForce = new DragForce(); /** * <p>This creates a FlingAnimation that animates a {@link FloatValueHolder} instance. During * the animation, the {@link FloatValueHolder} instance will be updated via * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date * animation value via {@link FloatValueHolder#getValue()}. * * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via * {@link FloatValueHolder#setValue(float)} outside of the animation during an * animation run will not have any effect on the on-going animation. * * @param floatValueHolder the property to be animated */ public FlingAnimation(FloatValueHolder floatValueHolder) { super(floatValueHolder); mFlingForce.setValueThreshold(getValueThreshold()); } /** * Sets the friction for the fling animation. The greater the friction is, the sooner the * animation will slow down. When not set, the friction defaults to 1. * * @param friction the friction used in the animation * @return the animation whose friction will be scaled * @throws IllegalArgumentException if the input friction is not positive */ public FlingAnimation setFriction( @FloatRange(from = 0.0, fromInclusive = false) float friction) { if (friction <= 0) { throw new IllegalArgumentException("Friction must be positive"); } mFlingForce.setFrictionScalar(friction); return this; } /** * Returns the friction being set on the animation via {@link #setFriction(float)}. If the * friction has not been set, the default friction of 1 will be returned. * * @return friction being used in the animation */ public float getFriction() { return mFlingForce.getFrictionScalar(); } /** * Sets the min value of the animation. When a fling animation reaches the min value, the * animation will end immediately. Animations will not animate beyond the min value. * * @param minValue minimum value of the property to be animated * @return the Animation whose min value is being set */ @Override public FlingAnimation setMinValue(float minValue) { super.setMinValue(minValue); return this; } /** * Sets the max value of the animation. When a fling animation reaches the max value, the * animation will end immediately. Animations will not animate beyond the max value. * * @param maxValue maximum value of the property to be animated * @return the Animation whose max value is being set */ @Override public FlingAnimation setMaxValue(float maxValue) { super.setMaxValue(maxValue); return this; } /** * Start velocity of the animation. Default velocity is 0. Unit: pixel/second * * <p>A <b>non-zero</b> start velocity is required for a FlingAnimation. If no start velocity is * set through {@link #setStartVelocity(float)}, the start velocity defaults to 0. In that * case, the fling animation will consider itself done in the next frame. * * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity * through touch events), it is recommended to define such a value in dp/second and convert it * to pixel/second based on the density of the screen to achieve a consistent look across * different screens. * * <p>To convert from dp/second to pixel/second: * <pre class="prettyprint"> * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, * getResources().getDisplayMetrics()); * </pre> * * @param startVelocity start velocity of the animation in pixel/second * @return the Animation whose start velocity is being set */ @Override public FlingAnimation setStartVelocity(float startVelocity) { super.setStartVelocity(startVelocity); return this; } @Override boolean updateValueAndVelocity(long deltaT) { MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT); mValue = state.mValue; mVelocity = state.mVelocity; // When the animation hits the max/min value, consider animation done. if (mValue < mMinValue) { mValue = mMinValue; return true; } if (mValue > mMaxValue) { mValue = mMaxValue; return true; } if (isAtEquilibrium(mValue, mVelocity)) { return true; } return false; } @Override float getAcceleration(float value, float velocity) { return mFlingForce.getAcceleration(value, velocity); } @Override boolean isAtEquilibrium(float value, float velocity) { return value >= mMaxValue || value <= mMinValue || mFlingForce.isAtEquilibrium(value, velocity); } @Override void setValueThreshold(float threshold) { mFlingForce.setValueThreshold(threshold); } private static final class DragForce implements Force { private static final float DEFAULT_FRICTION = -4.2f; // This multiplier is used to calculate the velocity threshold given a certain value // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount, // then the velocity is a reasonable threshold. private static final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f; private float mFriction = DEFAULT_FRICTION; private float mVelocityThreshold; // Internal state to hold a value/velocity pair. private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState(); void setFrictionScalar(float frictionScalar) { mFriction = frictionScalar * DEFAULT_FRICTION; } float getFrictionScalar() { return mFriction / DEFAULT_FRICTION; } MassState updateValueAndVelocity(float value, float velocity, long deltaT) { mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction)); mMassState.mValue = (float) (value - velocity / mFriction + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f)); if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) { mMassState.mVelocity = 0f; } return mMassState; } @Override public float getAcceleration(float position, float velocity) { return velocity * mFriction; } @Override public boolean isAtEquilibrium(float value, float velocity) { return Math.abs(velocity) < mVelocityThreshold; } void setValueThreshold(float threshold) { mVelocityThreshold = threshold * VELOCITY_THRESHOLD_MULTIPLIER; } } } core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; Loading Loading @@ -368,7 +369,7 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackInvoked(); waitForIdle(); verify(mCallback1).onBackInvoked(); verify(mCallback1, timeout(/*millis*/ 1000)).onBackInvoked(); verify(mCallback1, never()).onBackCancelled(); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +0 −18 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.DisplayMetrics; import android.util.Log; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; Loading @@ -62,7 +61,6 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.R; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; Loading @@ -87,15 +85,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public static final boolean IS_ENABLED = SystemProperties.getInt("persist.wm.debug.predictive_back", SETTING_VALUE_ON) == SETTING_VALUE_ON; public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms public static final float FLING_SPEED_UP_FACTOR = 0.6f; /** * The maximum additional progress in case of fling gesture. * The end animation starts after the user lifts the finger from the screen, we continue to * fire {@link BackEvent}s until the velocity reaches 0. */ private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */ /** Predictive back animation developer option */ private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); Loading @@ -118,8 +107,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mPointersPilfered = false; private final boolean mRequirePointerPilfer; private final FlingAnimationUtils mFlingAnimationUtils; /** Registry for the back animations */ private final ShellBackAnimationRegistry mShellBackAnimationRegistry; Loading Loading @@ -232,11 +219,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics) .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); mShellBackAnimationRegistry = shellBackAnimationRegistry; mLatencyTracker = LatencyTracker.getInstance(mContext); mShellCommandHandler = shellCommandHandler; Loading Loading
core/java/android/window/BackProgressAnimator.java +50 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,12 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.FloatProperty; import android.util.TimeUtils; import android.view.Choreographer; import com.android.internal.dynamicanimation.animation.DynamicAnimation; import com.android.internal.dynamicanimation.animation.FlingAnimation; import com.android.internal.dynamicanimation.animation.FloatValueHolder; import com.android.internal.dynamicanimation.animation.SpringAnimation; import com.android.internal.dynamicanimation.animation.SpringForce; Loading @@ -40,6 +44,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL * always receive progress values in [0, 1]. */ private static final float SCALE_FACTOR = 100f; private static final float FLING_FRICTION = 8f; private final SpringAnimation mSpring; private ProgressCallback mCallback; private float mProgress = 0; Loading @@ -48,11 +53,17 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL private boolean mBackAnimationInProgress = false; @Nullable private Runnable mBackCancelledFinishRunnable; @Nullable private Runnable mBackInvokedFinishRunnable; private FlingAnimation mBackInvokedFlingAnim; private final DynamicAnimation.OnAnimationEndListener mOnAnimationEndListener = (animation, canceled, value, velocity) -> { invokeBackCancelledRunnable(); if (mBackCancelledFinishRunnable != null) invokeBackCancelledRunnable(); if (mBackInvokedFinishRunnable != null) invokeBackInvokedRunnable(); reset(); }; private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener = (animation, progress, velocity) -> updateProgressValue(progress, velocity); private void setProgress(float progress) { Loading @@ -78,7 +89,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL @Override public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { updateProgressValue(value, velocity); if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity); } Loading Loading @@ -134,6 +145,12 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL // Ensure that last progress value that apps see is 0 updateProgressValue(0, 0); invokeBackCancelledRunnable(); } else if (mBackInvokedFinishRunnable != null) { invokeBackInvokedRunnable(); } if (mBackInvokedFlingAnim != null) { mBackInvokedFlingAnim.cancel(); mBackInvokedFlingAnim = null; } mSpring.animateToFinalPosition(0); if (mSpring.canSkipToEnd()) { Loading @@ -148,6 +165,30 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mProgress = 0; } /** * Animate the back progress animation a bit further with a high friction considering the * current progress and velocity. * * @param finishCallback the callback to be invoked when the final destination is reached */ public void onBackInvoked(@NonNull Runnable finishCallback) { mBackInvokedFinishRunnable = finishCallback; mSpring.animateToFinalPosition(0); mBackInvokedFlingAnim = new FlingAnimation(new FloatValueHolder()) .setStartValue(mProgress) .setFriction(FLING_FRICTION) .setStartVelocity(mVelocity) .setMinValue(0) .setMaxValue(SCALE_FACTOR); mBackInvokedFlingAnim.addUpdateListener(mOnBackInvokedFlingUpdateListener); mBackInvokedFlingAnim.addEndListener(mOnAnimationEndListener); mBackInvokedFlingAnim.start(); // do an animation-frame immediately to prevent idle frame mBackInvokedFlingAnim.doAnimationFrame( Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS); } /** * Animate the back progress animation from current progress to start position. * This should be called when back is cancelled. Loading Loading @@ -196,4 +237,11 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mBackCancelledFinishRunnable = null; } private void invokeBackInvokedRunnable() { mBackInvokedFlingAnim.removeUpdateListener(mOnBackInvokedFlingUpdateListener); mBackInvokedFlingAnim.removeEndListener(mOnAnimationEndListener); mBackInvokedFinishRunnable.run(); mBackInvokedFinishRunnable = null; } } No newline at end of file
core/java/android/window/WindowOnBackInvokedDispatcher.java +8 −3 Original line number Diff line number Diff line Loading @@ -452,10 +452,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mHandler.post(() -> { mTouchTracker.reset(); boolean isInProgress = mProgressAnimator.isBackAnimationInProgress(); mProgressAnimator.reset(); // TODO(b/333957271): Re-introduce auto fling progress generation. final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { mProgressAnimator.reset(); Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference."); return; } Loading @@ -463,7 +462,13 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Log.w(TAG, "ProgressAnimator was not in progress, skip onBackInvoked()."); return; } OnBackAnimationCallback animationCallback = getBackAnimationCallback(); if (animationCallback != null) { mProgressAnimator.onBackInvoked(callback::onBackInvoked); } else { mProgressAnimator.reset(); callback.onBackInvoked(); } }); } Loading
core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java 0 → 100644 +203 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.internal.dynamicanimation.animation; import android.annotation.FloatRange; /** * <p>Fling animation is an animation that continues an initial momentum (most often from gesture * velocity) and gradually slows down. The fling animation will come to a stop when the velocity of * the animation is below the threshold derived from {@link #setMinimumVisibleChange(float)}, * or when the value of the animation has gone beyond the min or max value defined via * {@link DynamicAnimation#setMinValue(float)} or {@link DynamicAnimation#setMaxValue(float)}. * It is recommended to restrict the fling animation with min and/or max value, such that the * animation can end when it goes beyond screen bounds, thus preserving CPU cycles and resources. * * <p>For example, you can create a fling animation that animates the translationX of a view: * <pre class="prettyprint"> * FlingAnimation flingAnim = new FlingAnimation(view, DynamicAnimation.TRANSLATION_X) * // Sets the start velocity to -2000 (pixel/s) * .setStartVelocity(-2000) * // Optional but recommended to set a reasonable min and max range for the animation. * // In this particular case, we set the min and max to -200 and 2000 respectively. * .setMinValue(-200).setMaxValue(2000); * flingAnim.start(); * </pre> */ public final class FlingAnimation extends DynamicAnimation<FlingAnimation> { private final DragForce mFlingForce = new DragForce(); /** * <p>This creates a FlingAnimation that animates a {@link FloatValueHolder} instance. During * the animation, the {@link FloatValueHolder} instance will be updated via * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date * animation value via {@link FloatValueHolder#getValue()}. * * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via * {@link FloatValueHolder#setValue(float)} outside of the animation during an * animation run will not have any effect on the on-going animation. * * @param floatValueHolder the property to be animated */ public FlingAnimation(FloatValueHolder floatValueHolder) { super(floatValueHolder); mFlingForce.setValueThreshold(getValueThreshold()); } /** * Sets the friction for the fling animation. The greater the friction is, the sooner the * animation will slow down. When not set, the friction defaults to 1. * * @param friction the friction used in the animation * @return the animation whose friction will be scaled * @throws IllegalArgumentException if the input friction is not positive */ public FlingAnimation setFriction( @FloatRange(from = 0.0, fromInclusive = false) float friction) { if (friction <= 0) { throw new IllegalArgumentException("Friction must be positive"); } mFlingForce.setFrictionScalar(friction); return this; } /** * Returns the friction being set on the animation via {@link #setFriction(float)}. If the * friction has not been set, the default friction of 1 will be returned. * * @return friction being used in the animation */ public float getFriction() { return mFlingForce.getFrictionScalar(); } /** * Sets the min value of the animation. When a fling animation reaches the min value, the * animation will end immediately. Animations will not animate beyond the min value. * * @param minValue minimum value of the property to be animated * @return the Animation whose min value is being set */ @Override public FlingAnimation setMinValue(float minValue) { super.setMinValue(minValue); return this; } /** * Sets the max value of the animation. When a fling animation reaches the max value, the * animation will end immediately. Animations will not animate beyond the max value. * * @param maxValue maximum value of the property to be animated * @return the Animation whose max value is being set */ @Override public FlingAnimation setMaxValue(float maxValue) { super.setMaxValue(maxValue); return this; } /** * Start velocity of the animation. Default velocity is 0. Unit: pixel/second * * <p>A <b>non-zero</b> start velocity is required for a FlingAnimation. If no start velocity is * set through {@link #setStartVelocity(float)}, the start velocity defaults to 0. In that * case, the fling animation will consider itself done in the next frame. * * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity * through touch events), it is recommended to define such a value in dp/second and convert it * to pixel/second based on the density of the screen to achieve a consistent look across * different screens. * * <p>To convert from dp/second to pixel/second: * <pre class="prettyprint"> * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, * getResources().getDisplayMetrics()); * </pre> * * @param startVelocity start velocity of the animation in pixel/second * @return the Animation whose start velocity is being set */ @Override public FlingAnimation setStartVelocity(float startVelocity) { super.setStartVelocity(startVelocity); return this; } @Override boolean updateValueAndVelocity(long deltaT) { MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT); mValue = state.mValue; mVelocity = state.mVelocity; // When the animation hits the max/min value, consider animation done. if (mValue < mMinValue) { mValue = mMinValue; return true; } if (mValue > mMaxValue) { mValue = mMaxValue; return true; } if (isAtEquilibrium(mValue, mVelocity)) { return true; } return false; } @Override float getAcceleration(float value, float velocity) { return mFlingForce.getAcceleration(value, velocity); } @Override boolean isAtEquilibrium(float value, float velocity) { return value >= mMaxValue || value <= mMinValue || mFlingForce.isAtEquilibrium(value, velocity); } @Override void setValueThreshold(float threshold) { mFlingForce.setValueThreshold(threshold); } private static final class DragForce implements Force { private static final float DEFAULT_FRICTION = -4.2f; // This multiplier is used to calculate the velocity threshold given a certain value // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount, // then the velocity is a reasonable threshold. private static final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f; private float mFriction = DEFAULT_FRICTION; private float mVelocityThreshold; // Internal state to hold a value/velocity pair. private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState(); void setFrictionScalar(float frictionScalar) { mFriction = frictionScalar * DEFAULT_FRICTION; } float getFrictionScalar() { return mFriction / DEFAULT_FRICTION; } MassState updateValueAndVelocity(float value, float velocity, long deltaT) { mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction)); mMassState.mValue = (float) (value - velocity / mFriction + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f)); if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) { mMassState.mVelocity = 0f; } return mMassState; } @Override public float getAcceleration(float position, float velocity) { return velocity * mFriction; } @Override public boolean isAtEquilibrium(float value, float velocity) { return Math.abs(velocity) < mVelocityThreshold; } void setValueThreshold(float threshold) { mVelocityThreshold = threshold * VELOCITY_THRESHOLD_MULTIPLIER; } } }
core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; Loading Loading @@ -368,7 +369,7 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackInvoked(); waitForIdle(); verify(mCallback1).onBackInvoked(); verify(mCallback1, timeout(/*millis*/ 1000)).onBackInvoked(); verify(mCallback1, never()).onBackCancelled(); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +0 −18 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.DisplayMetrics; import android.util.Log; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; Loading @@ -62,7 +61,6 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.R; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; Loading @@ -87,15 +85,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public static final boolean IS_ENABLED = SystemProperties.getInt("persist.wm.debug.predictive_back", SETTING_VALUE_ON) == SETTING_VALUE_ON; public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms public static final float FLING_SPEED_UP_FACTOR = 0.6f; /** * The maximum additional progress in case of fling gesture. * The end animation starts after the user lifts the finger from the screen, we continue to * fire {@link BackEvent}s until the velocity reaches 0. */ private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */ /** Predictive back animation developer option */ private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); Loading @@ -118,8 +107,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mPointersPilfered = false; private final boolean mRequirePointerPilfer; private final FlingAnimationUtils mFlingAnimationUtils; /** Registry for the back animations */ private final ShellBackAnimationRegistry mShellBackAnimationRegistry; Loading Loading @@ -232,11 +219,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics) .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); mShellBackAnimationRegistry = shellBackAnimationRegistry; mLatencyTracker = LatencyTracker.getInstance(mContext); mShellCommandHandler = shellCommandHandler; Loading