diff options
12 files changed, 116 insertions, 21 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index c91992c29630..46fda83d3444 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -61645,6 +61645,8 @@ package android.window { public final class BackEvent { ctor public BackEvent(float, float, float, int); + ctor @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public BackEvent(float, float, float, int, long); + method @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public long getFrameTime(); method @FloatRange(from=0, to=1) public float getProgress(); method public int getSwipeEdge(); method public float getTouchX(); diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java index d3733b71f7ee..1c3f201c1471 100644 --- a/core/java/android/window/BackEvent.java +++ b/core/java/android/window/BackEvent.java @@ -16,8 +16,13 @@ package android.window; +import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; +import android.util.TimeUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -44,18 +49,25 @@ public final class BackEvent { private final float mTouchX; private final float mTouchY; private final float mProgress; + private final long mFrameTime; @SwipeEdge private final int mSwipeEdge; /** @hide */ public static BackEvent fromBackMotionEvent(BackMotionEvent backMotionEvent) { - return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), - backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge()); + if (predictiveBackTimestampApi()) { + return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), + backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge(), + backMotionEvent.getFrameTime()); + } else { + return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), + backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge()); + } } /** - * Creates a new {@link BackEvent} instance. + * Creates a new {@link BackEvent} instance with the current uptime as frame time. * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. @@ -67,6 +79,26 @@ public final class BackEvent { mTouchY = touchY; mProgress = progress; mSwipeEdge = swipeEdge; + mFrameTime = System.nanoTime() / TimeUtils.NANOS_PER_MS; + } + + /** + * Creates a new {@link BackEvent} instance. + * + * @param touchX Absolute X location of the touch point of this event. + * @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 frameTime frame time of the back event. + */ + @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) + public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge, + long frameTime) { + mTouchX = touchX; + mTouchY = touchY; + mProgress = progress; + mSwipeEdge = swipeEdge; + mFrameTime = frameTime; } /** @@ -115,13 +147,22 @@ public final class BackEvent { return mSwipeEdge; } + /** + * Returns the frameTime of the BackEvent in milliseconds. Useful for calculating velocity. + */ + @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) + public long getFrameTime() { + return mFrameTime; + } + @Override public String toString() { return "BackEvent{" + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress - + ", mSwipeEdge" + mSwipeEdge + + ", mSwipeEdge=" + mSwipeEdge + + ", mFrameTime=" + mFrameTime + "ms" + "}"; } } diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index 8ac68abd8d8e..a8ec4eeb039a 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -33,6 +33,7 @@ import android.view.RemoteAnimationTarget; public final class BackMotionEvent implements Parcelable { private final float mTouchX; private final float mTouchY; + private final long mFrameTime; private final float mProgress; private final boolean mTriggerBack; @@ -48,6 +49,7 @@ public final class BackMotionEvent implements Parcelable { * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. + * @param frameTime Event time of the corresponding touch event. * @param progress Value between 0 and 1 on how far along the back gesture is. * @param triggerBack Indicates whether the back arrow is in the triggered state or not * @param swipeEdge Indicates which edge the swipe starts from. @@ -57,12 +59,14 @@ public final class BackMotionEvent implements Parcelable { public BackMotionEvent( float touchX, float touchY, + long frameTime, float progress, boolean triggerBack, @BackEvent.SwipeEdge int swipeEdge, @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; + mFrameTime = frameTime; mProgress = progress; mTriggerBack = triggerBack; mSwipeEdge = swipeEdge; @@ -76,6 +80,7 @@ public final class BackMotionEvent implements Parcelable { mTriggerBack = in.readBoolean(); mSwipeEdge = in.readInt(); mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); + mFrameTime = in.readLong(); } @NonNull @@ -104,6 +109,7 @@ public final class BackMotionEvent implements Parcelable { dest.writeBoolean(mTriggerBack); dest.writeInt(mSwipeEdge); dest.writeTypedObject(mDepartingAnimationTarget, flags); + dest.writeLong(mFrameTime); } /** @@ -148,6 +154,13 @@ public final class BackMotionEvent implements Parcelable { } /** + * Returns the frame time of the BackMotionEvent in milliseconds. + */ + public long getFrameTime() { + return mFrameTime; + } + + /** * 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. @@ -162,10 +175,11 @@ public final class BackMotionEvent implements Parcelable { return "BackMotionEvent{" + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + + ", mFrameTime=" + mFrameTime + "ms" + ", mProgress=" + mProgress + ", mTriggerBack=" + mTriggerBack - + ", mSwipeEdge" + mSwipeEdge - + ", mDepartingAnimationTarget" + mDepartingAnimationTarget + + ", mSwipeEdge=" + mSwipeEdge + + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget + "}"; } } diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index 465e11a048f0..a5be58b7b183 100644 --- a/core/java/android/window/BackProgressAnimator.java +++ b/core/java/android/window/BackProgressAnimator.java @@ -17,6 +17,7 @@ package android.window; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -66,7 +67,8 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL reset(); }; private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener = - (animation, progress, velocity) -> updateProgressValue(progress, velocity); + (animation, progress, velocity) -> + updateProgressValue(progress, velocity, animation.getLastFrameTime()); private void setProgress(float progress) { @@ -92,7 +94,9 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL @Override public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { - if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity); + if (mBackInvokedFinishRunnable == null) { + updateProgressValue(value, velocity, animation.getLastFrameTime()); + } } @@ -137,7 +141,9 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mLastBackEvent = event; mCallback = callback; mBackAnimationInProgress = true; - updateProgressValue(0, 0); + updateProgressValue(/* progress */ 0, /* velocity */ 0, + /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS); + onBackProgressed(event); } /** @@ -146,7 +152,8 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL public void reset() { if (mBackCancelledFinishRunnable != null) { // Ensure that last progress value that apps see is 0 - updateProgressValue(0, 0); + updateProgressValue(/* progress */ 0, /* velocity */ 0, + /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS); invokeBackCancelledRunnable(); } else if (mBackInvokedFinishRunnable != null) { invokeBackInvokedRunnable(); @@ -236,14 +243,20 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL return mVelocity / SCALE_FACTOR; } - private void updateProgressValue(float progress, float velocity) { + private void updateProgressValue(float progress, float velocity, long frameTime) { mVelocity = velocity; if (mLastBackEvent == null || mCallback == null || !mBackAnimationInProgress) { return; } - mCallback.onProgressUpdate( - new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), - progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge())); + BackEvent backEvent; + if (predictiveBackTimestampApi()) { + backEvent = new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), + progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge(), frameTime); + } else { + backEvent = new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), + progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge()); + } + mCallback.onProgressUpdate(backEvent); } private void invokeBackCancelledRunnable() { diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java index 290c494b1bff..39a30253adbd 100644 --- a/core/java/android/window/BackTouchTracker.java +++ b/core/java/android/window/BackTouchTracker.java @@ -151,6 +151,7 @@ public class BackTouchTracker { return new BackMotionEvent( /* touchX = */ mInitTouchX, /* touchY = */ mInitTouchY, + /* frameTime = */ 0, /* progress = */ 0, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, @@ -235,6 +236,7 @@ public class BackTouchTracker { return new BackMotionEvent( /* touchX = */ mLatestTouchX, /* touchY = */ mLatestTouchY, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 66c35e2fe837..8db1f9509757 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -17,6 +17,7 @@ package android.window; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -235,8 +236,12 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @Override public void onBackStarted(@NonNull BackEvent backEvent) { try { + long frameTime = 0; + if (predictiveBackTimestampApi()) { + frameTime = backEvent.getFrameTime(); + } mIOnBackInvokedCallback.onBackStarted( - new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), + new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { @@ -247,8 +252,12 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @Override public void onBackProgressed(@NonNull BackEvent backEvent) { try { + long frameTime = 0; + if (predictiveBackTimestampApi()) { + frameTime = backEvent.getFrameTime(); + } mIOnBackInvokedCallback.onBackProgressed( - new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), + new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 622f8c817d99..460469c13a3e 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -325,3 +325,11 @@ flag { is_fixed_read_only: true bug: "362938401" } + +flag { + name: "predictive_back_timestamp_api" + namespace: "systemui" + description: "expose timestamp in BackEvent (API extension)" + is_fixed_read_only: true + bug: "362938401" +} diff --git a/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java b/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java index d4fe7c8d7f36..7a4c1a00a41f 100644 --- a/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java +++ b/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java @@ -762,6 +762,10 @@ public abstract class DynamicAnimation<T extends DynamicAnimation<T>> return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance(); } + public long getLastFrameTime() { + return mLastFrameTime; + } + /****************Sub class animations**************/ /** * Returns the acceleration at the given value with the given velocity. diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index b001cc2929e3..46bd73e316f6 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -21,6 +21,7 @@ import static android.window.OnBackInvokedDispatcher.PRIORITY_OVERLAY; import static android.window.OnBackInvokedDispatcher.PRIORITY_SYSTEM_NAVIGATION_OBSERVER; import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER; +import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -486,6 +487,7 @@ public class WindowOnBackInvokedDispatcherTest { } @Test + @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) public void onBackInvoked_notCalledAfterCallbackUnregistration() throws RemoteException, InterruptedException { // Setup a callback that unregisters itself after the gesture is finished but before the @@ -684,6 +686,7 @@ public class WindowOnBackInvokedDispatcherTest { return new BackMotionEvent( /* touchX = */ 0, /* touchY = */ 0, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index b90e6e2fab8a..19b51f143241 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -355,11 +355,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont int keyAction, @BackEvent.SwipeEdge int swipeEdge ) { - mShellExecutor.execute(() -> onMotionEvent( - /* touchX = */ touchX, - /* touchY = */ touchY, - /* keyAction = */ keyAction, - /* swipeEdge = */ swipeEdge)); + mShellExecutor.execute( + () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge)); } @Override diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java index 1da4ef6b5a8b..266e48482568 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java @@ -53,6 +53,7 @@ public class BackProgressAnimatorTest { return new BackMotionEvent( /* touchX = */ touchX, /* touchY = */ 0, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt index 2235c20d7f21..2cc52c5ab9ad 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt @@ -221,6 +221,7 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() { BackMotionEvent( /* touchX = */ touchX, /* touchY = */ 0f, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, |