Loading core/java/android/window/BackProgressAnimator.java 0 → 100644 +136 −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; 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())); } } core/java/android/window/IOnBackInvokedCallback.aidl +7 −6 Original line number Diff line number Diff line Loading @@ -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); Loading core/java/android/window/OnBackAnimationCallback.java +6 −4 Original line number Diff line number Diff line Loading @@ -42,8 +42,10 @@ import android.view.View; public interface OnBackAnimationCallback extends OnBackInvokedCallback { /** * Called when a back gesture has been started, or back button has been pressed down. * * @param backEvent An {@link BackEvent} object describing the progress event. */ default void onBackStarted() { } default void onBackStarted(@NonNull BackEvent backEvent) {} /** * Called on back gesture progress. Loading core/java/android/window/OnBackInvokedCallback.java +28 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.window; import android.annotation.NonNull; import android.app.Activity; import android.app.Dialog; import android.view.Window; Loading @@ -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() {} } core/java/android/window/WindowOnBackInvokedDispatcher.java +10 −3 Original line number Diff line number Diff line Loading @@ -218,19 +218,24 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public Checker getChecker() { return mChecker; } @NonNull private static final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference<OnBackInvokedCallback> mCallback; OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) { mCallback = new WeakReference<>(callback); } @Override public void onBackStarted() { public void onBackStarted(BackEvent backEvent) { Handler.getMain().post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackStarted(); mProgressAnimator.onBackStarted(backEvent, event -> callback.onBackProgressed(event)); callback.onBackStarted(backEvent); } }); } Loading @@ -240,7 +245,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Handler.getMain().post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackProgressed(backEvent); mProgressAnimator.onBackProgressed(backEvent); } }); } Loading @@ -248,6 +253,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackCancelled() { Handler.getMain().post(() -> { mProgressAnimator.reset(); final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackCancelled(); Loading @@ -258,6 +264,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackInvoked() throws RemoteException { Handler.getMain().post(() -> { mProgressAnimator.reset(); final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { return; Loading Loading
core/java/android/window/BackProgressAnimator.java 0 → 100644 +136 −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; 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())); } }
core/java/android/window/IOnBackInvokedCallback.aidl +7 −6 Original line number Diff line number Diff line Loading @@ -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); Loading
core/java/android/window/OnBackAnimationCallback.java +6 −4 Original line number Diff line number Diff line Loading @@ -42,8 +42,10 @@ import android.view.View; public interface OnBackAnimationCallback extends OnBackInvokedCallback { /** * Called when a back gesture has been started, or back button has been pressed down. * * @param backEvent An {@link BackEvent} object describing the progress event. */ default void onBackStarted() { } default void onBackStarted(@NonNull BackEvent backEvent) {} /** * Called on back gesture progress. Loading
core/java/android/window/OnBackInvokedCallback.java +28 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.window; import android.annotation.NonNull; import android.app.Activity; import android.app.Dialog; import android.view.Window; Loading @@ -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() {} }
core/java/android/window/WindowOnBackInvokedDispatcher.java +10 −3 Original line number Diff line number Diff line Loading @@ -218,19 +218,24 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public Checker getChecker() { return mChecker; } @NonNull private static final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference<OnBackInvokedCallback> mCallback; OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) { mCallback = new WeakReference<>(callback); } @Override public void onBackStarted() { public void onBackStarted(BackEvent backEvent) { Handler.getMain().post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackStarted(); mProgressAnimator.onBackStarted(backEvent, event -> callback.onBackProgressed(event)); callback.onBackStarted(backEvent); } }); } Loading @@ -240,7 +245,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Handler.getMain().post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackProgressed(backEvent); mProgressAnimator.onBackProgressed(backEvent); } }); } Loading @@ -248,6 +253,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackCancelled() { Handler.getMain().post(() -> { mProgressAnimator.reset(); final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { callback.onBackCancelled(); Loading @@ -258,6 +264,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackInvoked() throws RemoteException { Handler.getMain().post(() -> { mProgressAnimator.reset(); final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { return; Loading