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

Commit 62b6dc23 authored by Shan Huang's avatar Shan Huang Committed by Android (Google) Code Review
Browse files

Merge "Spring controlled back progress API"

parents 5f9d98d5 c6357f1c
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package android.window;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.RemoteAnimationTarget;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -50,6 +52,8 @@ public class BackEvent implements Parcelable {

    @SwipeEdge
    private final int mSwipeEdge;
    @Nullable
    private final RemoteAnimationTarget mDepartingAnimationTarget;

    /**
     * Creates a new {@link BackEvent} instance.
@@ -58,12 +62,16 @@ public class BackEvent implements Parcelable {
     * @param touchY Absolute Y location of the touch point of this event.
     * @param progress Value between 0 and 1 on how far along the back gesture is.
     * @param swipeEdge Indicates which edge the swipe starts from.
     * @param departingAnimationTarget The remote animation target of the departing
     *                                 application window.
     */
    public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge) {
    public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge,
            @Nullable RemoteAnimationTarget departingAnimationTarget) {
        mTouchX = touchX;
        mTouchY = touchY;
        mProgress = progress;
        mSwipeEdge = swipeEdge;
        mDepartingAnimationTarget = departingAnimationTarget;
    }

    private BackEvent(@NonNull Parcel in) {
@@ -71,6 +79,7 @@ public class BackEvent implements Parcelable {
        mTouchY = in.readFloat();
        mProgress = in.readFloat();
        mSwipeEdge = in.readInt();
        mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
    }

    public static final Creator<BackEvent> CREATOR = new Creator<BackEvent>() {
@@ -96,6 +105,7 @@ public class BackEvent implements Parcelable {
        dest.writeFloat(mTouchY);
        dest.writeFloat(mProgress);
        dest.writeInt(mSwipeEdge);
        dest.writeTypedObject(mDepartingAnimationTarget, flags);
    }

    /**
@@ -126,6 +136,16 @@ public class BackEvent implements Parcelable {
        return mSwipeEdge;
    }

    /**
     * Returns the {@link RemoteAnimationTarget} of the top departing application window,
     * or {@code null} if the top window should not be moved for the current type of back
     * destination.
     */
    @Nullable
    public RemoteAnimationTarget getDepartingAnimationTarget() {
        return mDepartingAnimationTarget;
    }

    @Override
    public String toString() {
        return "BackEvent{"
+139 −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 android.window;

import android.util.FloatProperty;

import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.internal.dynamicanimation.animation.SpringForce;

/**
 * An animator that drives the predictive back progress with a spring.
 *
 * The back gesture's latest touch point and committal state determines the final position of
 * the spring. The continuous movement of the spring is used to produce {@link BackEvent}s with
 * smoothly transitioning progress values.
 *
 * @hide
 */
public class BackProgressAnimator {
    /**
     *  A factor to scale the input progress by, so that it works better with the spring.
     *  We divide the output progress by this value before sending it to apps, so that apps
     *  always receive progress values in [0, 1].
     */
    private static final float SCALE_FACTOR = 100f;
    private final SpringAnimation mSpring;
    private ProgressCallback mCallback;
    private float mProgress = 0;
    private BackEvent mLastBackEvent;
    private boolean mStarted = false;

    private void setProgress(float progress) {
        mProgress = progress;
    }

    private float getProgress() {
        return mProgress;
    }

    private static final FloatProperty<BackProgressAnimator> PROGRESS_PROP =
            new FloatProperty<BackProgressAnimator>("progress") {
                @Override
                public void setValue(BackProgressAnimator animator, float value) {
                    animator.setProgress(value);
                    animator.updateProgressValue(value);
                }

                @Override
                public Float get(BackProgressAnimator object) {
                    return object.getProgress();
                }
            };


    /** A callback to be invoked when there's a progress value update from the animator. */
    public interface ProgressCallback {
        /** Called when there's a progress value update. */
        void onProgressUpdate(BackEvent event);
    }

    public BackProgressAnimator() {
        mSpring = new SpringAnimation(this, PROGRESS_PROP);
        mSpring.setSpring(new SpringForce()
                .setStiffness(SpringForce.STIFFNESS_MEDIUM)
                .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY));
    }

    /**
     * Sets a new target position for the back progress.
     *
     * @param event the {@link BackEvent} containing the latest target progress.
     */
    public void onBackProgressed(BackEvent event) {
        if (!mStarted) {
            return;
        }
        mLastBackEvent = event;
        if (mSpring == null) {
            return;
        }
        mSpring.animateToFinalPosition(event.getProgress() * SCALE_FACTOR);
    }

    /**
     * Starts the back progress animation.
     *
     * @param event the {@link BackEvent} that started the gesture.
     * @param callback the back callback to invoke for the gesture. It will receive back progress
     *                 dispatches as the progress animation updates.
     */
    public void onBackStarted(BackEvent event, ProgressCallback callback) {
        reset();
        mLastBackEvent = event;
        mCallback = callback;
        mStarted = true;
    }

    /**
     * Resets the back progress animation. This should be called when back is invoked or cancelled.
     */
    public void reset() {
        mSpring.animateToFinalPosition(0);
        if (mSpring.canSkipToEnd()) {
            mSpring.skipToEnd();
        } else {
            // Should never happen.
            mSpring.cancel();
        }
        mStarted = false;
        mLastBackEvent = null;
        mCallback = null;
        mProgress = 0;
    }

    private void updateProgressValue(float progress) {
        if (mLastBackEvent == null || mCallback == null || !mStarted) {
            return;
        }
        mCallback.onProgressUpdate(
                new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(),
                        progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge(),
                        mLastBackEvent.getDepartingAnimationTarget()));
    }

}
+7 −6
Original line number Diff line number Diff line
@@ -28,17 +28,18 @@ import android.window.BackEvent;
oneway interface IOnBackInvokedCallback {
   /**
    * Called when a back gesture has been started, or back button has been pressed down.
    * Wraps {@link OnBackInvokedCallback#onBackStarted()}.
    * Wraps {@link OnBackInvokedCallback#onBackStarted(BackEvent)}.
    *
    * @param backEvent The {@link BackEvent} containing information about the touch or button press.
    */
    void onBackStarted();
    void onBackStarted(in BackEvent backEvent);

    /**
     * Called on back gesture progress.
     * Wraps {@link OnBackInvokedCallback#onBackProgressed()}.
     * Wraps {@link OnBackInvokedCallback#onBackProgressed(BackEvent)}.
     *
     * @param touchX Absolute X location of the touch point.
     * @param touchY Absolute Y location of the touch point.
     * @param progress Value between 0 and 1 on how far along the back gesture is.
     * @param backEvent The {@link BackEvent} containing information about the latest touch point
     *                  and the progress that the back animation should seek to.
     */
    void onBackProgressed(in BackEvent backEvent);

+3 −8
Original line number Diff line number Diff line
@@ -13,14 +13,11 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.window;

import android.annotation.NonNull;
import android.app.Activity;
import android.app.Dialog;
import android.view.View;

/**
 * Interface for applications to register back animation callbacks along their custom back
 * handling.
@@ -44,7 +41,6 @@ public interface OnBackAnimationCallback extends OnBackInvokedCallback {
     * Called when a back gesture has been started, or back button has been pressed down.
     */
    default void onBackStarted() { }

    /**
     * Called on back gesture progress.
     *
@@ -53,7 +49,6 @@ public interface OnBackAnimationCallback extends OnBackInvokedCallback {
     * @see BackEvent
     */
    default void onBackProgressed(@NonNull BackEvent backEvent) { }

    /**
     * Called when a back gesture or back button press has been cancelled.
     */
+28 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.window;

import android.annotation.NonNull;
import android.app.Activity;
import android.app.Dialog;
import android.view.Window;
@@ -40,9 +41,36 @@ import android.view.Window;
 */
@SuppressWarnings("deprecation")
public interface OnBackInvokedCallback {
    /**
     * Called when a back gesture has been started, or back button has been pressed down.
     *
     * @param backEvent The {@link BackEvent} containing information about the touch or
     *                  button press.
     *
     * @hide
     */
    default void onBackStarted(@NonNull BackEvent backEvent) {}

    /**
     * Called when a back gesture has been progressed.
     *
     * @param backEvent The {@link BackEvent} containing information about the latest touch point
     *                  and the progress that the back animation should seek to.
     *
     * @hide
     */
    default void onBackProgressed(@NonNull BackEvent backEvent) {}

    /**
     * Called when a back gesture has been completed and committed, or back button pressed
     * has been released and committed.
     */
    void onBackInvoked();

    /**
     * Called when a back gesture or button press has been cancelled.
     *
     * @hide
     */
    default void onBackCancelled() {}
}
Loading