Merge "DO NOT MERGE Spring controlled back progress API" into tm-qpr-dev
This commit is contained in:
136
core/java/android/window/BackProgressAnimator.java
Normal file
136
core/java/android/window/BackProgressAnimator.java
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,17 +28,18 @@ import android.window.BackEvent;
|
|||||||
oneway interface IOnBackInvokedCallback {
|
oneway interface IOnBackInvokedCallback {
|
||||||
/**
|
/**
|
||||||
* Called when a back gesture has been started, or back button has been pressed down.
|
* 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.
|
* Called on back gesture progress.
|
||||||
* Wraps {@link OnBackInvokedCallback#onBackProgressed()}.
|
* Wraps {@link OnBackInvokedCallback#onBackProgressed(BackEvent)}.
|
||||||
*
|
*
|
||||||
* @param touchX Absolute X location of the touch point.
|
* @param backEvent The {@link BackEvent} containing information about the latest touch point
|
||||||
* @param touchY Absolute Y location of the touch point.
|
* and the progress that the back animation should seek to.
|
||||||
* @param progress Value between 0 and 1 on how far along the back gesture is.
|
|
||||||
*/
|
*/
|
||||||
void onBackProgressed(in BackEvent backEvent);
|
void onBackProgressed(in BackEvent backEvent);
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,12 @@ import android.view.View;
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public interface OnBackAnimationCallback extends OnBackInvokedCallback {
|
public interface OnBackAnimationCallback extends OnBackInvokedCallback {
|
||||||
/**
|
/**
|
||||||
* Called when a back gesture has been started, or back button has been pressed down.
|
* Called when a back gesture has been started, or back button has been pressed down.
|
||||||
*/
|
*
|
||||||
default void onBackStarted() { }
|
* @param backEvent An {@link BackEvent} object describing the progress event.
|
||||||
|
*/
|
||||||
|
default void onBackStarted(@NonNull BackEvent backEvent) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on back gesture progress.
|
* Called on back gesture progress.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.window;
|
package android.window;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
@@ -40,9 +41,36 @@ import android.view.Window;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public interface OnBackInvokedCallback {
|
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
|
* Called when a back gesture has been completed and committed, or back button pressed
|
||||||
* has been released and committed.
|
* has been released and committed.
|
||||||
*/
|
*/
|
||||||
void onBackInvoked();
|
void onBackInvoked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a back gesture or button press has been cancelled.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
default void onBackCancelled() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,19 +218,24 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
public Checker getChecker() {
|
public Checker getChecker() {
|
||||||
return mChecker;
|
return mChecker;
|
||||||
}
|
}
|
||||||
|
@NonNull
|
||||||
|
private static final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
|
||||||
|
|
||||||
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
|
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
|
||||||
private final WeakReference<OnBackInvokedCallback> mCallback;
|
private final WeakReference<OnBackInvokedCallback> mCallback;
|
||||||
|
|
||||||
OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
|
OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
|
||||||
mCallback = new WeakReference<>(callback);
|
mCallback = new WeakReference<>(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackStarted() {
|
public void onBackStarted(BackEvent backEvent) {
|
||||||
Handler.getMain().post(() -> {
|
Handler.getMain().post(() -> {
|
||||||
final OnBackAnimationCallback callback = getBackAnimationCallback();
|
final OnBackAnimationCallback callback = getBackAnimationCallback();
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onBackStarted();
|
mProgressAnimator.onBackStarted(backEvent, event ->
|
||||||
|
callback.onBackProgressed(event));
|
||||||
|
callback.onBackStarted(backEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -240,7 +245,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
Handler.getMain().post(() -> {
|
Handler.getMain().post(() -> {
|
||||||
final OnBackAnimationCallback callback = getBackAnimationCallback();
|
final OnBackAnimationCallback callback = getBackAnimationCallback();
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onBackProgressed(backEvent);
|
mProgressAnimator.onBackProgressed(backEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -248,6 +253,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
@Override
|
@Override
|
||||||
public void onBackCancelled() {
|
public void onBackCancelled() {
|
||||||
Handler.getMain().post(() -> {
|
Handler.getMain().post(() -> {
|
||||||
|
mProgressAnimator.reset();
|
||||||
final OnBackAnimationCallback callback = getBackAnimationCallback();
|
final OnBackAnimationCallback callback = getBackAnimationCallback();
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onBackCancelled();
|
callback.onBackCancelled();
|
||||||
@@ -258,6 +264,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
@Override
|
@Override
|
||||||
public void onBackInvoked() throws RemoteException {
|
public void onBackInvoked() throws RemoteException {
|
||||||
Handler.getMain().post(() -> {
|
Handler.getMain().post(() -> {
|
||||||
|
mProgressAnimator.reset();
|
||||||
final OnBackInvokedCallback callback = mCallback.get();
|
final OnBackInvokedCallback callback = mCallback.get();
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public class WindowOnBackInvokedDispatcherTest {
|
|||||||
private OnBackAnimationCallback mCallback1;
|
private OnBackAnimationCallback mCallback1;
|
||||||
@Mock
|
@Mock
|
||||||
private OnBackAnimationCallback mCallback2;
|
private OnBackAnimationCallback mCallback2;
|
||||||
|
@Mock
|
||||||
|
private BackEvent mBackEvent;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@@ -85,14 +87,14 @@ public class WindowOnBackInvokedDispatcherTest {
|
|||||||
verify(mWindowSession, times(2)).setOnBackInvokedCallbackInfo(
|
verify(mWindowSession, times(2)).setOnBackInvokedCallbackInfo(
|
||||||
Mockito.eq(mWindow),
|
Mockito.eq(mWindow),
|
||||||
captor.capture());
|
captor.capture());
|
||||||
captor.getAllValues().get(0).getCallback().onBackStarted();
|
captor.getAllValues().get(0).getCallback().onBackStarted(mBackEvent);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mCallback1).onBackStarted();
|
verify(mCallback1).onBackStarted(mBackEvent);
|
||||||
verifyZeroInteractions(mCallback2);
|
verifyZeroInteractions(mCallback2);
|
||||||
|
|
||||||
captor.getAllValues().get(1).getCallback().onBackStarted();
|
captor.getAllValues().get(1).getCallback().onBackStarted(mBackEvent);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mCallback2).onBackStarted();
|
verify(mCallback2).onBackStarted(mBackEvent);
|
||||||
verifyNoMoreInteractions(mCallback1);
|
verifyNoMoreInteractions(mCallback1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +112,9 @@ public class WindowOnBackInvokedDispatcherTest {
|
|||||||
Mockito.eq(mWindow), captor.capture());
|
Mockito.eq(mWindow), captor.capture());
|
||||||
verifyNoMoreInteractions(mWindowSession);
|
verifyNoMoreInteractions(mWindowSession);
|
||||||
assertEquals(captor.getValue().getPriority(), OnBackInvokedDispatcher.PRIORITY_OVERLAY);
|
assertEquals(captor.getValue().getPriority(), OnBackInvokedDispatcher.PRIORITY_OVERLAY);
|
||||||
captor.getValue().getCallback().onBackStarted();
|
captor.getValue().getCallback().onBackStarted(mBackEvent);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mCallback1).onBackStarted();
|
verify(mCallback1).onBackStarted(mBackEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -148,8 +150,8 @@ public class WindowOnBackInvokedDispatcherTest {
|
|||||||
mDispatcher.registerOnBackInvokedCallback(
|
mDispatcher.registerOnBackInvokedCallback(
|
||||||
OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback2);
|
OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback2);
|
||||||
verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture());
|
verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture());
|
||||||
captor.getValue().getCallback().onBackStarted();
|
captor.getValue().getCallback().onBackStarted(mBackEvent);
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
verify(mCallback2).onBackStarted();
|
verify(mCallback2).onBackStarted(mBackEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,13 +75,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
private static final String TAG = "BackAnimationController";
|
private static final String TAG = "BackAnimationController";
|
||||||
private static final int SETTING_VALUE_OFF = 0;
|
private static final int SETTING_VALUE_OFF = 0;
|
||||||
private static final int SETTING_VALUE_ON = 1;
|
private static final int SETTING_VALUE_ON = 1;
|
||||||
private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
|
|
||||||
"persist.wm.debug.predictive_back_progress_threshold";
|
|
||||||
public static final boolean IS_ENABLED =
|
public static final boolean IS_ENABLED =
|
||||||
SystemProperties.getInt("persist.wm.debug.predictive_back",
|
SystemProperties.getInt("persist.wm.debug.predictive_back",
|
||||||
SETTING_VALUE_ON) != SETTING_VALUE_OFF;
|
SETTING_VALUE_ON) == SETTING_VALUE_ON;
|
||||||
private static final int PROGRESS_THRESHOLD = SystemProperties
|
/** Flag for U animation features */
|
||||||
.getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
|
public static boolean IS_U_ANIMATION_ENABLED =
|
||||||
|
SystemProperties.getInt("persist.wm.debug.predictive_back_anim",
|
||||||
|
SETTING_VALUE_OFF) == SETTING_VALUE_ON;
|
||||||
|
/** Predictive back animation developer option */
|
||||||
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
|
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
|
||||||
// TODO (b/241808055) Find a appropriate time to remove during refactor
|
// TODO (b/241808055) Find a appropriate time to remove during refactor
|
||||||
private static final boolean USE_TRANSITION =
|
private static final boolean USE_TRANSITION =
|
||||||
@@ -114,7 +115,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
@Nullable
|
@Nullable
|
||||||
private IOnBackInvokedCallback mBackToLauncherCallback;
|
private IOnBackInvokedCallback mBackToLauncherCallback;
|
||||||
private float mTriggerThreshold;
|
private float mTriggerThreshold;
|
||||||
private float mProgressThreshold;
|
|
||||||
private final Runnable mResetTransitionRunnable = () -> {
|
private final Runnable mResetTransitionRunnable = () -> {
|
||||||
finishAnimation();
|
finishAnimation();
|
||||||
mTransitionInProgress = false;
|
mTransitionInProgress = false;
|
||||||
@@ -125,7 +125,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
private IBackNaviAnimationController mBackAnimationController;
|
private IBackNaviAnimationController mBackAnimationController;
|
||||||
private BackAnimationAdaptor mBackAnimationAdaptor;
|
private BackAnimationAdaptor mBackAnimationAdaptor;
|
||||||
|
|
||||||
private boolean mWaitingAnimationStart;
|
|
||||||
private final TouchTracker mTouchTracker = new TouchTracker();
|
private final TouchTracker mTouchTracker = new TouchTracker();
|
||||||
private final CachingBackDispatcher mCachingBackDispatcher = new CachingBackDispatcher();
|
private final CachingBackDispatcher mCachingBackDispatcher = new CachingBackDispatcher();
|
||||||
|
|
||||||
@@ -147,44 +146,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to record the touch location for gesture start and latest.
|
|
||||||
*/
|
|
||||||
private static class TouchTracker {
|
|
||||||
/**
|
|
||||||
* Location of the latest touch event
|
|
||||||
*/
|
|
||||||
private float mLatestTouchX;
|
|
||||||
private float mLatestTouchY;
|
|
||||||
private int mSwipeEdge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Location of the initial touch event of the back gesture.
|
|
||||||
*/
|
|
||||||
private float mInitTouchX;
|
|
||||||
private float mInitTouchY;
|
|
||||||
|
|
||||||
void update(float touchX, float touchY, int swipeEdge) {
|
|
||||||
mLatestTouchX = touchX;
|
|
||||||
mLatestTouchY = touchY;
|
|
||||||
mSwipeEdge = swipeEdge;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGestureStartLocation(float touchX, float touchY) {
|
|
||||||
mInitTouchX = touchX;
|
|
||||||
mInitTouchY = touchY;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getDeltaFromGestureStart(float touchX) {
|
|
||||||
return Math.round(touchX - mInitTouchX);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
mInitTouchX = 0;
|
|
||||||
mInitTouchY = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache the temporary callback and trigger result if gesture was finish before received
|
* Cache the temporary callback and trigger result if gesture was finish before received
|
||||||
* BackAnimationRunner#onAnimationStart/cancel, so there can continue play the animation.
|
* BackAnimationRunner#onAnimationStart/cancel, so there can continue play the animation.
|
||||||
@@ -212,15 +173,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
boolean consumed = false;
|
boolean consumed = false;
|
||||||
if (mWaitingAnimation && mOnBackCallback != null) {
|
if (mWaitingAnimation && mOnBackCallback != null) {
|
||||||
if (mTriggerBack) {
|
if (mTriggerBack) {
|
||||||
final BackEvent backFinish = new BackEvent(
|
final BackEvent backFinish = mTouchTracker.createProgressEvent(1);
|
||||||
mTouchTracker.mLatestTouchX, mTouchTracker.mLatestTouchY, 1,
|
|
||||||
mTouchTracker.mSwipeEdge, mAnimationTarget);
|
|
||||||
dispatchOnBackProgressed(mBackToLauncherCallback, backFinish);
|
dispatchOnBackProgressed(mBackToLauncherCallback, backFinish);
|
||||||
dispatchOnBackInvoked(mOnBackCallback);
|
dispatchOnBackInvoked(mOnBackCallback);
|
||||||
} else {
|
} else {
|
||||||
final BackEvent backFinish = new BackEvent(
|
final BackEvent backFinish = mTouchTracker.createProgressEvent(0);
|
||||||
mTouchTracker.mLatestTouchX, mTouchTracker.mLatestTouchY, 0,
|
|
||||||
mTouchTracker.mSwipeEdge, mAnimationTarget);
|
|
||||||
dispatchOnBackProgressed(mBackToLauncherCallback, backFinish);
|
dispatchOnBackProgressed(mBackToLauncherCallback, backFinish);
|
||||||
dispatchOnBackCancelled(mOnBackCallback);
|
dispatchOnBackCancelled(mOnBackCallback);
|
||||||
}
|
}
|
||||||
@@ -263,6 +220,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
shellInit.addInitCallback(this::onInit, this);
|
shellInit.addInitCallback(this::onInit, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setEnableUAnimation(boolean enable) {
|
||||||
|
IS_U_ANIMATION_ENABLED = enable;
|
||||||
|
}
|
||||||
|
|
||||||
private void onInit() {
|
private void onInit() {
|
||||||
setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
|
setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
|
||||||
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
|
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
|
||||||
@@ -404,7 +366,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mTouchTracker.update(touchX, touchY, swipeEdge);
|
mTouchTracker.update(touchX, touchY);
|
||||||
if (keyAction == MotionEvent.ACTION_DOWN) {
|
if (keyAction == MotionEvent.ACTION_DOWN) {
|
||||||
if (!mBackGestureStarted) {
|
if (!mBackGestureStarted) {
|
||||||
mShouldStartOnNextMoveEvent = true;
|
mShouldStartOnNextMoveEvent = true;
|
||||||
@@ -414,7 +376,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
// Let the animation initialized here to make sure the onPointerDownOutsideFocus
|
// Let the animation initialized here to make sure the onPointerDownOutsideFocus
|
||||||
// could be happened when ACTION_DOWN, it may change the current focus that we
|
// could be happened when ACTION_DOWN, it may change the current focus that we
|
||||||
// would access it when startBackNavigation.
|
// would access it when startBackNavigation.
|
||||||
onGestureStarted(touchX, touchY);
|
onGestureStarted(touchX, touchY, swipeEdge);
|
||||||
mShouldStartOnNextMoveEvent = false;
|
mShouldStartOnNextMoveEvent = false;
|
||||||
}
|
}
|
||||||
onMove(touchX, touchY, swipeEdge);
|
onMove(touchX, touchY, swipeEdge);
|
||||||
@@ -428,14 +390,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGestureStarted(float touchX, float touchY) {
|
private void onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
|
||||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
|
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
|
||||||
if (mBackGestureStarted || mBackNavigationInfo != null) {
|
if (mBackGestureStarted || mBackNavigationInfo != null) {
|
||||||
Log.e(TAG, "Animation is being initialized but is already started.");
|
Log.e(TAG, "Animation is being initialized but is already started.");
|
||||||
finishAnimation();
|
finishAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
mTouchTracker.setGestureStartLocation(touchX, touchY);
|
mTouchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
|
||||||
mBackGestureStarted = true;
|
mBackGestureStarted = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -464,6 +426,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
displayTargetScreenshot(hardwareBuffer,
|
displayTargetScreenshot(hardwareBuffer,
|
||||||
backNavigationInfo.getTaskWindowConfiguration());
|
backNavigationInfo.getTaskWindowConfiguration());
|
||||||
}
|
}
|
||||||
|
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||||
mTransaction.apply();
|
mTransaction.apply();
|
||||||
} else if (dispatchToLauncher) {
|
} else if (dispatchToLauncher) {
|
||||||
targetCallback = mBackToLauncherCallback;
|
targetCallback = mBackToLauncherCallback;
|
||||||
@@ -474,7 +437,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||||
}
|
}
|
||||||
if (!USE_TRANSITION || !dispatchToLauncher) {
|
if (!USE_TRANSITION || !dispatchToLauncher) {
|
||||||
dispatchOnBackStarted(targetCallback);
|
dispatchOnBackStarted(
|
||||||
|
targetCallback,
|
||||||
|
mTouchTracker.createStartEvent(
|
||||||
|
mBackNavigationInfo.getDepartingAnimationTarget()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,29 +480,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
if (!mBackGestureStarted || mBackNavigationInfo == null) {
|
if (!mBackGestureStarted || mBackNavigationInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int deltaX = mTouchTracker.getDeltaFromGestureStart(touchX);
|
final BackEvent backEvent = mTouchTracker.createProgressEvent();
|
||||||
float progressThreshold = PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold;
|
if (USE_TRANSITION && mBackAnimationController != null && mAnimationTarget != null) {
|
||||||
float progress = Math.min(Math.max(Math.abs(deltaX) / progressThreshold, 0), 1);
|
|
||||||
if (USE_TRANSITION) {
|
|
||||||
if (mBackAnimationController != null && mAnimationTarget != null) {
|
|
||||||
final BackEvent backEvent = new BackEvent(
|
|
||||||
touchX, touchY, progress, swipeEdge, mAnimationTarget);
|
|
||||||
dispatchOnBackProgressed(mBackToLauncherCallback, backEvent);
|
dispatchOnBackProgressed(mBackToLauncherCallback, backEvent);
|
||||||
}
|
} else if (mEnableAnimations.get()) {
|
||||||
} else {
|
|
||||||
int backType = mBackNavigationInfo.getType();
|
int backType = mBackNavigationInfo.getType();
|
||||||
RemoteAnimationTarget animationTarget =
|
IOnBackInvokedCallback targetCallback;
|
||||||
mBackNavigationInfo.getDepartingAnimationTarget();
|
|
||||||
|
|
||||||
BackEvent backEvent = new BackEvent(
|
|
||||||
touchX, touchY, progress, swipeEdge, animationTarget);
|
|
||||||
IOnBackInvokedCallback targetCallback = null;
|
|
||||||
if (shouldDispatchToLauncher(backType)) {
|
if (shouldDispatchToLauncher(backType)) {
|
||||||
targetCallback = mBackToLauncherCallback;
|
targetCallback = mBackToLauncherCallback;
|
||||||
} else if (backType == BackNavigationInfo.TYPE_CROSS_TASK
|
} else {
|
||||||
|| backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
|
|
||||||
// TODO(208427216) Run the actual animation
|
|
||||||
} else if (backType == BackNavigationInfo.TYPE_CALLBACK) {
|
|
||||||
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||||
}
|
}
|
||||||
dispatchOnBackProgressed(targetCallback, backEvent);
|
dispatchOnBackProgressed(targetCallback, backEvent);
|
||||||
@@ -620,18 +572,21 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
|| mBackNavigationInfo.getDepartingAnimationTarget() != null);
|
|| mBackNavigationInfo.getDepartingAnimationTarget() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) {
|
private void dispatchOnBackStarted(IOnBackInvokedCallback callback,
|
||||||
|
BackEvent backEvent) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
callback.onBackStarted();
|
if (shouldDispatchAnimation(callback)) {
|
||||||
|
callback.onBackStarted(backEvent);
|
||||||
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "dispatchOnBackStarted error: ", e);
|
Log.e(TAG, "dispatchOnBackStarted error: ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dispatchOnBackInvoked(IOnBackInvokedCallback callback) {
|
private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -642,29 +597,38 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dispatchOnBackCancelled(IOnBackInvokedCallback callback) {
|
private void dispatchOnBackCancelled(IOnBackInvokedCallback callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
callback.onBackCancelled();
|
if (shouldDispatchAnimation(callback)) {
|
||||||
|
callback.onBackCancelled();
|
||||||
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "dispatchOnBackCancelled error: ", e);
|
Log.e(TAG, "dispatchOnBackCancelled error: ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
|
private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
|
||||||
BackEvent backEvent) {
|
BackEvent backEvent) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
callback.onBackProgressed(backEvent);
|
if (shouldDispatchAnimation(callback)) {
|
||||||
|
callback.onBackProgressed(backEvent);
|
||||||
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "dispatchOnBackProgressed error: ", e);
|
Log.e(TAG, "dispatchOnBackProgressed error: ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldDispatchAnimation(IOnBackInvokedCallback callback) {
|
||||||
|
return (IS_U_ANIMATION_ENABLED || callback == mBackToLauncherCallback)
|
||||||
|
&& mEnableAnimations.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
|
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
|
||||||
*/
|
*/
|
||||||
@@ -673,10 +637,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mTriggerBack = triggerBack;
|
mTriggerBack = triggerBack;
|
||||||
|
mTouchTracker.setTriggerBack(triggerBack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSwipeThresholds(float triggerThreshold, float progressThreshold) {
|
private void setSwipeThresholds(float triggerThreshold, float progressThreshold) {
|
||||||
mProgressThreshold = progressThreshold;
|
mTouchTracker.setProgressThreshold(progressThreshold);
|
||||||
mTriggerThreshold = triggerThreshold;
|
mTriggerThreshold = triggerThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +651,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
|
BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
|
||||||
boolean triggerBack = mTriggerBack;
|
boolean triggerBack = mTriggerBack;
|
||||||
mBackNavigationInfo = null;
|
mBackNavigationInfo = null;
|
||||||
|
mAnimationTarget = null;
|
||||||
mTriggerBack = false;
|
mTriggerBack = false;
|
||||||
mShouldStartOnNextMoveEvent = false;
|
mShouldStartOnNextMoveEvent = false;
|
||||||
if (backNavigationInfo == null) {
|
if (backNavigationInfo == null) {
|
||||||
@@ -762,17 +728,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
|||||||
tx.apply();
|
tx.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO animation target should be passed at onBackStarted
|
dispatchOnBackStarted(mBackToLauncherCallback,
|
||||||
dispatchOnBackStarted(mBackToLauncherCallback);
|
mTouchTracker.createStartEvent(mAnimationTarget));
|
||||||
// TODO This is Workaround for LauncherBackAnimationController, there will need
|
final BackEvent backInit = mTouchTracker.createProgressEvent();
|
||||||
// to dispatch onBackProgressed twice(startBack & updateBackProgress) to
|
|
||||||
// initialize the animation data, for now that would happen when onMove
|
|
||||||
// called, but there will no expected animation if the down -> up gesture
|
|
||||||
// happen very fast which ACTION_MOVE only happen once.
|
|
||||||
final BackEvent backInit = new BackEvent(
|
|
||||||
mTouchTracker.mLatestTouchX, mTouchTracker.mLatestTouchY, 0,
|
|
||||||
mTouchTracker.mSwipeEdge, mAnimationTarget);
|
|
||||||
dispatchOnBackProgressed(mBackToLauncherCallback, backInit);
|
|
||||||
if (!mCachingBackDispatcher.consume()) {
|
if (!mCachingBackDispatcher.consume()) {
|
||||||
dispatchOnBackProgressed(mBackToLauncherCallback, backInit);
|
dispatchOnBackProgressed(mBackToLauncherCallback, backInit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.android.wm.shell.back;
|
||||||
|
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
import android.view.RemoteAnimationTarget;
|
||||||
|
import android.window.BackEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to record the touch location for gesture and generate back events.
|
||||||
|
*/
|
||||||
|
class TouchTracker {
|
||||||
|
private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
|
||||||
|
"persist.wm.debug.predictive_back_progress_threshold";
|
||||||
|
private static final int PROGRESS_THRESHOLD = SystemProperties
|
||||||
|
.getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
|
||||||
|
private float mProgressThreshold;
|
||||||
|
/**
|
||||||
|
* Location of the latest touch event
|
||||||
|
*/
|
||||||
|
private float mLatestTouchX;
|
||||||
|
private float mLatestTouchY;
|
||||||
|
private boolean mTriggerBack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of the initial touch event of the back gesture.
|
||||||
|
*/
|
||||||
|
private float mInitTouchX;
|
||||||
|
private float mInitTouchY;
|
||||||
|
private float mStartThresholdX;
|
||||||
|
private int mSwipeEdge;
|
||||||
|
private boolean mCancelled;
|
||||||
|
|
||||||
|
void update(float touchX, float touchY) {
|
||||||
|
/**
|
||||||
|
* If back was previously cancelled but the user has started swiping in the forward
|
||||||
|
* direction again, restart back.
|
||||||
|
*/
|
||||||
|
if (mCancelled && ((touchX > mLatestTouchX && mSwipeEdge == BackEvent.EDGE_LEFT)
|
||||||
|
|| touchX < mLatestTouchX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
|
||||||
|
mCancelled = false;
|
||||||
|
mStartThresholdX = touchX;
|
||||||
|
}
|
||||||
|
mLatestTouchX = touchX;
|
||||||
|
mLatestTouchY = touchY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTriggerBack(boolean triggerBack) {
|
||||||
|
if (mTriggerBack != triggerBack && !triggerBack) {
|
||||||
|
mCancelled = true;
|
||||||
|
}
|
||||||
|
mTriggerBack = triggerBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setGestureStartLocation(float touchX, float touchY, int swipeEdge) {
|
||||||
|
mInitTouchX = touchX;
|
||||||
|
mInitTouchY = touchY;
|
||||||
|
mSwipeEdge = swipeEdge;
|
||||||
|
mStartThresholdX = mInitTouchX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
mInitTouchX = 0;
|
||||||
|
mInitTouchY = 0;
|
||||||
|
mStartThresholdX = 0;
|
||||||
|
mCancelled = false;
|
||||||
|
mTriggerBack = false;
|
||||||
|
mSwipeEdge = BackEvent.EDGE_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackEvent createStartEvent(RemoteAnimationTarget target) {
|
||||||
|
return new BackEvent(mInitTouchX, mInitTouchY, 0, mSwipeEdge, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
BackEvent createProgressEvent() {
|
||||||
|
float progressThreshold = PROGRESS_THRESHOLD >= 0
|
||||||
|
? PROGRESS_THRESHOLD : mProgressThreshold;
|
||||||
|
progressThreshold = progressThreshold == 0 ? 1 : progressThreshold;
|
||||||
|
float progress = 0;
|
||||||
|
// Progress is always 0 when back is cancelled and not restarted.
|
||||||
|
if (!mCancelled) {
|
||||||
|
// If back is committed, progress is the distance between the last and first touch
|
||||||
|
// point, divided by the max drag distance. Otherwise, it's the distance between
|
||||||
|
// the last touch point and the starting threshold, divided by max drag distance.
|
||||||
|
// The starting threshold is initially the first touch location, and updated to
|
||||||
|
// the location everytime back is restarted after being cancelled.
|
||||||
|
float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
|
||||||
|
float deltaX = Math.max(
|
||||||
|
mSwipeEdge == BackEvent.EDGE_LEFT
|
||||||
|
? mLatestTouchX - startX
|
||||||
|
: startX - mLatestTouchX,
|
||||||
|
0);
|
||||||
|
progress = Math.min(Math.max(deltaX / progressThreshold, 0), 1);
|
||||||
|
}
|
||||||
|
return createProgressEvent(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
BackEvent createProgressEvent(float progress) {
|
||||||
|
return new BackEvent(mLatestTouchX, mLatestTouchY, progress, mSwipeEdge, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgressThreshold(float progressThreshold) {
|
||||||
|
mProgressThreshold = progressThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -127,6 +127,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
|||||||
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
|
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
|
||||||
mActivityTaskManager, mContext,
|
mActivityTaskManager, mContext,
|
||||||
mContentResolver);
|
mContentResolver);
|
||||||
|
mController.setEnableUAnimation(true);
|
||||||
mShellInit.init();
|
mShellInit.init();
|
||||||
mEventTime = 0;
|
mEventTime = 0;
|
||||||
mShellExecutor.flushAll();
|
mShellExecutor.flushAll();
|
||||||
@@ -245,10 +246,10 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
|||||||
// Check that back start and progress is dispatched when first move.
|
// Check that back start and progress is dispatched when first move.
|
||||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
||||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
|
||||||
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
|
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
|
||||||
verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture());
|
verify(mIOnBackInvokedCallback).onBackStarted(backEventCaptor.capture());
|
||||||
assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
|
assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
|
||||||
|
verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(any(BackEvent.class));
|
||||||
|
|
||||||
// Check that back invocation is dispatched.
|
// Check that back invocation is dispatched.
|
||||||
mController.setTriggerBack(true); // Fake trigger back
|
mController.setTriggerBack(true); // Fake trigger back
|
||||||
@@ -276,11 +277,11 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
|||||||
|
|
||||||
triggerBackGesture();
|
triggerBackGesture();
|
||||||
|
|
||||||
verify(appCallback, never()).onBackStarted();
|
verify(appCallback, never()).onBackStarted(any(BackEvent.class));
|
||||||
verify(appCallback, never()).onBackProgressed(backEventCaptor.capture());
|
verify(appCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||||
verify(appCallback, times(1)).onBackInvoked();
|
verify(appCallback, times(1)).onBackInvoked();
|
||||||
|
|
||||||
verify(mIOnBackInvokedCallback, never()).onBackStarted();
|
verify(mIOnBackInvokedCallback, never()).onBackStarted(any(BackEvent.class));
|
||||||
verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture());
|
verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||||
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
|
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
|
||||||
}
|
}
|
||||||
@@ -313,7 +314,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
|||||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
||||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -332,7 +333,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
|||||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
||||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -348,7 +349,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
|||||||
// Check that back start and progress is dispatched when first move.
|
// Check that back start and progress is dispatched when first move.
|
||||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
|
||||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||||
|
|
||||||
// Check that back invocation is dispatched.
|
// Check that back invocation is dispatched.
|
||||||
mController.setTriggerBack(true); // Fake trigger back
|
mController.setTriggerBack(true); // Fake trigger back
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.android.wm.shell.back;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import android.window.BackEvent;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class TouchTrackerTest {
|
||||||
|
private static final float FAKE_THRESHOLD = 400;
|
||||||
|
private static final float INITIAL_X_LEFT_EDGE = 5;
|
||||||
|
private static final float INITIAL_X_RIGHT_EDGE = FAKE_THRESHOLD - INITIAL_X_LEFT_EDGE;
|
||||||
|
private TouchTracker mTouchTracker;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mTouchTracker = new TouchTracker();
|
||||||
|
mTouchTracker.setProgressThreshold(FAKE_THRESHOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generatesProgress_onStart() {
|
||||||
|
mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
|
||||||
|
BackEvent event = mTouchTracker.createStartEvent(null);
|
||||||
|
assertEquals(event.getProgress(), 0f, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generatesProgress_leftEdge() {
|
||||||
|
mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
|
||||||
|
float touchX = 10;
|
||||||
|
|
||||||
|
// Pre-commit
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
|
||||||
|
|
||||||
|
// Post-commit
|
||||||
|
touchX += 100;
|
||||||
|
mTouchTracker.setTriggerBack(true);
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
|
||||||
|
|
||||||
|
// Cancel
|
||||||
|
touchX -= 10;
|
||||||
|
mTouchTracker.setTriggerBack(false);
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), 0, 0f);
|
||||||
|
|
||||||
|
// Cancel more
|
||||||
|
touchX -= 10;
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), 0, 0f);
|
||||||
|
|
||||||
|
// Restart
|
||||||
|
touchX += 10;
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), 0, 0f);
|
||||||
|
|
||||||
|
// Restarted, but pre-commit
|
||||||
|
float restartX = touchX;
|
||||||
|
touchX += 10;
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f);
|
||||||
|
|
||||||
|
// Restarted, post-commit
|
||||||
|
touchX += 10;
|
||||||
|
mTouchTracker.setTriggerBack(true);
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generatesProgress_rightEdge() {
|
||||||
|
mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT);
|
||||||
|
float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge
|
||||||
|
|
||||||
|
// Pre-commit
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
|
||||||
|
|
||||||
|
// Post-commit
|
||||||
|
touchX -= 100;
|
||||||
|
mTouchTracker.setTriggerBack(true);
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
|
||||||
|
|
||||||
|
// Cancel
|
||||||
|
touchX += 10;
|
||||||
|
mTouchTracker.setTriggerBack(false);
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), 0, 0f);
|
||||||
|
|
||||||
|
// Cancel more
|
||||||
|
touchX += 10;
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), 0, 0f);
|
||||||
|
|
||||||
|
// Restart
|
||||||
|
touchX -= 10;
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), 0, 0f);
|
||||||
|
|
||||||
|
// Restarted, but pre-commit
|
||||||
|
float restartX = touchX;
|
||||||
|
touchX -= 10;
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f);
|
||||||
|
|
||||||
|
// Restarted, post-commit
|
||||||
|
touchX -= 10;
|
||||||
|
mTouchTracker.setTriggerBack(true);
|
||||||
|
mTouchTracker.update(touchX, 0);
|
||||||
|
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getProgress() {
|
||||||
|
return mTouchTracker.createProgressEvent().getProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -242,7 +242,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
|||||||
private IOnBackInvokedCallback createOnBackInvokedCallback() {
|
private IOnBackInvokedCallback createOnBackInvokedCallback() {
|
||||||
return new IOnBackInvokedCallback.Stub() {
|
return new IOnBackInvokedCallback.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void onBackStarted() {
|
public void onBackStarted(BackEvent backEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user