Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 713a1568 authored by Johannes Gallmann's avatar Johannes Gallmann Committed by Android (Google) Code Review
Browse files

Merge "Reenable auto generation of progress events on fling" into main

parents 61d00c02 f0ebeaac
Loading
Loading
Loading
Loading
+50 −2
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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) {
@@ -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);
    }


@@ -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()) {
@@ -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.
@@ -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
+8 −3
Original line number Diff line number Diff line
@@ -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;
                }
@@ -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();
                }
            });
        }

+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;
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -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;
@@ -368,7 +369,7 @@ public class WindowOnBackInvokedDispatcherTest {
        callbackInfo.getCallback().onBackInvoked();

        waitForIdle();
        verify(mCallback1).onBackInvoked();
        verify(mCallback1, timeout(/*millis*/ 1000)).onBackInvoked();
        verify(mCallback1, never()).onBackCancelled();
    }

+0 −18
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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);
@@ -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;

@@ -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