Merge "Rephrase BackAnimationController and BackAnimationRunner"
This commit is contained in:
@@ -38,7 +38,6 @@ import android.os.UserHandle;
|
||||
import android.provider.Settings.Global;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.IWindowFocusObserver;
|
||||
import android.view.InputDevice;
|
||||
@@ -69,7 +68,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* Controls the window animation run when a user initiates a back gesture.
|
||||
*/
|
||||
public class BackAnimationController implements RemoteCallable<BackAnimationController> {
|
||||
private static final String TAG = "BackAnimationController";
|
||||
private static final String TAG = "ShellBackPreview";
|
||||
private static final int SETTING_VALUE_OFF = 0;
|
||||
private static final int SETTING_VALUE_ON = 1;
|
||||
public static final boolean IS_ENABLED =
|
||||
@@ -82,16 +81,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
/** Predictive back animation developer option */
|
||||
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
|
||||
/**
|
||||
* Max duration to wait for a transition to finish before accepting another gesture start
|
||||
* request.
|
||||
* Max duration to wait for an animation to finish before triggering the real back.
|
||||
*/
|
||||
private static final long MAX_TRANSITION_DURATION = 2000;
|
||||
private static final long MAX_ANIMATION_DURATION = 2000;
|
||||
|
||||
/** True when a back gesture is ongoing */
|
||||
private boolean mBackGestureStarted = false;
|
||||
|
||||
/** Tracks if an uninterruptible transition is in progress */
|
||||
private boolean mTransitionInProgress = false;
|
||||
/** Tracks if an uninterruptible animation is in progress */
|
||||
private boolean mPostCommitAnimationInProgress = false;
|
||||
/** Tracks if we should start the back gesture on the next motion move event */
|
||||
private boolean mShouldStartOnNextMoveEvent = false;
|
||||
/** @see #setTriggerBack(boolean) */
|
||||
@@ -105,9 +103,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
private final ShellController mShellController;
|
||||
private final ShellExecutor mShellExecutor;
|
||||
private final Handler mBgHandler;
|
||||
private final Runnable mResetTransitionRunnable = () -> {
|
||||
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Transition didn't finish in %d ms. Resetting...",
|
||||
MAX_TRANSITION_DURATION);
|
||||
private final Runnable mAnimationTimeoutRunnable = () -> {
|
||||
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
|
||||
MAX_ANIMATION_DURATION);
|
||||
onBackAnimationFinished();
|
||||
};
|
||||
|
||||
@@ -119,6 +117,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
|
||||
private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
|
||||
|
||||
private IOnBackInvokedCallback mActiveCallback;
|
||||
|
||||
@VisibleForTesting
|
||||
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
|
||||
@Override
|
||||
@@ -126,9 +126,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
@Override
|
||||
public void focusLost(IBinder inputToken) {
|
||||
mShellExecutor.execute(() -> {
|
||||
if (!mBackGestureStarted || mTransitionInProgress) {
|
||||
// If an uninterruptible transition is already in progress, we should ignore
|
||||
// this due to the transition may cause focus lost. (alpha = 0)
|
||||
if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
|
||||
// If an uninterruptible animation is already in progress, we should ignore
|
||||
// this due to it may cause focus lost. (alpha = 0)
|
||||
return;
|
||||
}
|
||||
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Target window lost focus.");
|
||||
@@ -180,26 +180,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
|
||||
private void initBackAnimationRunners() {
|
||||
final IOnBackInvokedCallback dummyCallback = new IOnBackInvokedCallback.Default();
|
||||
final IRemoteAnimationRunner dummyRunner = new IRemoteAnimationRunner.Default() {
|
||||
@Override
|
||||
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
|
||||
// Animation missing. Simply finish animation.
|
||||
finishedCallback.onAnimationFinished();
|
||||
}
|
||||
};
|
||||
|
||||
final BackAnimationRunner dummyBackRunner =
|
||||
new BackAnimationRunner(dummyCallback, dummyRunner);
|
||||
final CrossTaskBackAnimation crossTaskAnimation = new CrossTaskBackAnimation(mContext);
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_TASK,
|
||||
new BackAnimationRunner(crossTaskAnimation.mCallback, crossTaskAnimation.mRunner));
|
||||
// TODO (238474994): register cross activity animation when it's completed.
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY, dummyBackRunner);
|
||||
// TODO (236760237): register dialog close animation when it's completed.
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_DIALOG_CLOSE, dummyBackRunner);
|
||||
}
|
||||
|
||||
private void setupAnimationDeveloperSettingsObserver(
|
||||
@@ -222,10 +207,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
private void updateEnableAnimationFromSetting() {
|
||||
int settingValue = Global.getInt(mContext.getContentResolver(),
|
||||
Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
|
||||
boolean isEnabled = settingValue == SETTING_VALUE_ON;
|
||||
boolean isEnabled = settingValue == SETTING_VALUE_ON && IS_U_ANIMATION_ENABLED;
|
||||
mEnableAnimations.set(isEnabled);
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s",
|
||||
isEnabled);
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled);
|
||||
}
|
||||
|
||||
public BackAnimation getBackAnimationImpl() {
|
||||
@@ -279,7 +263,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
|
||||
IRemoteAnimationRunner runner) {
|
||||
executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
|
||||
(controller) -> controller.setBackToLauncherCallback(callback, runner));
|
||||
(controller) -> controller.registerAnimation(
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME,
|
||||
new BackAnimationRunner(callback, runner)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -294,44 +280,22 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setBackToLauncherCallback(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner) {
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_RETURN_TO_HOME,
|
||||
new BackAnimationRunner(callback, runner));
|
||||
void registerAnimation(@BackNavigationInfo.BackTargetType int type,
|
||||
@NonNull BackAnimationRunner runner) {
|
||||
mAnimationDefinition.set(type, runner);
|
||||
}
|
||||
|
||||
private void clearBackToLauncherCallback() {
|
||||
mAnimationDefinition.remove(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void onBackAnimationFinished() {
|
||||
if (!mTransitionInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
|
||||
|
||||
// Trigger real back.
|
||||
if (mBackNavigationInfo != null) {
|
||||
IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
if (mTriggerBack) {
|
||||
dispatchOnBackInvoked(callback);
|
||||
} else {
|
||||
dispatchOnBackCancelled(callback);
|
||||
}
|
||||
}
|
||||
|
||||
finishBackNavigation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new motion event needs to be transferred to this
|
||||
* {@link BackAnimationController}
|
||||
*/
|
||||
public void onMotionEvent(float touchX, float touchY, int keyAction,
|
||||
@BackEvent.SwipeEdge int swipeEdge) {
|
||||
if (mTransitionInProgress) {
|
||||
if (mPostCommitAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -348,7 +312,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
onGestureStarted(touchX, touchY, swipeEdge);
|
||||
mShouldStartOnNextMoveEvent = false;
|
||||
}
|
||||
onMove(touchX, touchY, swipeEdge);
|
||||
onMove();
|
||||
} else if (keyAction == MotionEvent.ACTION_UP || keyAction == MotionEvent.ACTION_CANCEL) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
|
||||
"Finishing gesture with event action: %d", keyAction);
|
||||
@@ -386,30 +350,22 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
return;
|
||||
}
|
||||
final int backType = backNavigationInfo.getType();
|
||||
final IOnBackInvokedCallback targetCallback;
|
||||
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(backType);
|
||||
if (shouldDispatchToAnimator) {
|
||||
mActiveCallback = mAnimationDefinition.get(backType).getCallback();
|
||||
mAnimationDefinition.get(backType).startGesture();
|
||||
} else {
|
||||
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
dispatchOnBackStarted(targetCallback, mTouchTracker.createStartEvent(null));
|
||||
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
dispatchOnBackStarted(mActiveCallback, mTouchTracker.createStartEvent(null));
|
||||
}
|
||||
}
|
||||
|
||||
private void onMove(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
|
||||
private void onMove() {
|
||||
if (!mBackGestureStarted || mBackNavigationInfo == null || !mEnableAnimations.get()) {
|
||||
return;
|
||||
}
|
||||
final BackEvent backEvent = mTouchTracker.createProgressEvent();
|
||||
|
||||
int backType = mBackNavigationInfo.getType();
|
||||
IOnBackInvokedCallback targetCallback;
|
||||
if (shouldDispatchToAnimator(backType)) {
|
||||
targetCallback = mAnimationDefinition.get(backType).getCallback();
|
||||
} else {
|
||||
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
}
|
||||
dispatchOnBackProgressed(targetCallback, backEvent);
|
||||
dispatchOnBackProgressed(mActiveCallback, backEvent);
|
||||
}
|
||||
|
||||
private void injectBackKey() {
|
||||
@@ -431,57 +387,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
}
|
||||
|
||||
private void onGestureFinished(boolean fromTouch) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
|
||||
if (!mBackGestureStarted) {
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromTouch) {
|
||||
// Let touch reset the flag otherwise it will start a new back navigation and refresh
|
||||
// the info when received a new move event.
|
||||
mBackGestureStarted = false;
|
||||
}
|
||||
|
||||
if (mTransitionInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBackNavigationInfo == null) {
|
||||
// No focus window found or core are running recents animation, inject back key as
|
||||
// legacy behavior.
|
||||
if (mTriggerBack) {
|
||||
injectBackKey();
|
||||
}
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
int backType = mBackNavigationInfo.getType();
|
||||
boolean shouldDispatchToAnimator = shouldDispatchToAnimator(backType);
|
||||
final BackAnimationRunner runner = mAnimationDefinition.get(backType);
|
||||
IOnBackInvokedCallback targetCallback = shouldDispatchToAnimator
|
||||
? runner.getCallback() : mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
|
||||
if (shouldDispatchToAnimator) {
|
||||
if (runner.onGestureFinished(mTriggerBack)) {
|
||||
Log.w(TAG, "Gesture released, but animation didn't ready.");
|
||||
return;
|
||||
}
|
||||
startTransition();
|
||||
}
|
||||
if (mTriggerBack) {
|
||||
dispatchOnBackInvoked(targetCallback);
|
||||
} else {
|
||||
dispatchOnBackCancelled(targetCallback);
|
||||
}
|
||||
if (!shouldDispatchToAnimator) {
|
||||
// Animation callback missing. Simply finish animation.
|
||||
finishBackNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldDispatchToAnimator(int backType) {
|
||||
return mEnableAnimations.get()
|
||||
&& mBackNavigationInfo != null
|
||||
@@ -495,7 +400,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (shouldDispatchAnimation(callback)) {
|
||||
if (mEnableAnimations.get()) {
|
||||
callback.onBackStarted(backEvent);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
@@ -519,7 +424,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (shouldDispatchAnimation(callback)) {
|
||||
if (mEnableAnimations.get()) {
|
||||
callback.onBackCancelled();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
@@ -533,7 +438,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (shouldDispatchAnimation(callback)) {
|
||||
if (mEnableAnimations.get()) {
|
||||
callback.onBackProgressed(backEvent);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
@@ -541,17 +446,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldDispatchAnimation(IOnBackInvokedCallback callback) {
|
||||
return (IS_U_ANIMATION_ENABLED || callback == mAnimationDefinition.get(
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME).getCallback())
|
||||
&& mEnableAnimations.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
|
||||
*/
|
||||
public void setTriggerBack(boolean triggerBack) {
|
||||
if (mTransitionInProgress) {
|
||||
if (mPostCommitAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
mTriggerBack = triggerBack;
|
||||
@@ -562,6 +461,109 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
mTouchTracker.setProgressThreshold(progressThreshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the gesture is released, then it could start the post commit animation.
|
||||
*/
|
||||
private void onGestureFinished(boolean fromTouch) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
|
||||
if (!mBackGestureStarted) {
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromTouch) {
|
||||
// Let touch reset the flag otherwise it will start a new back navigation and refresh
|
||||
// the info when received a new move event.
|
||||
mBackGestureStarted = false;
|
||||
}
|
||||
|
||||
if (mPostCommitAnimationInProgress) {
|
||||
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBackNavigationInfo == null) {
|
||||
// No focus window found or core are running recents animation, inject back key as
|
||||
// legacy behavior.
|
||||
if (mTriggerBack) {
|
||||
injectBackKey();
|
||||
}
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
final int backType = mBackNavigationInfo.getType();
|
||||
// Directly finish back navigation if no animator defined.
|
||||
if (!shouldDispatchToAnimator(backType)) {
|
||||
if (mTriggerBack) {
|
||||
dispatchOnBackInvoked(mActiveCallback);
|
||||
} else {
|
||||
dispatchOnBackCancelled(mActiveCallback);
|
||||
}
|
||||
// Animation missing. Simply finish back navigation.
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
final BackAnimationRunner runner = mAnimationDefinition.get(backType);
|
||||
if (runner.isWaitingAnimation()) {
|
||||
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
|
||||
return;
|
||||
}
|
||||
startPostCommitAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the phase 2 animation when gesture is released.
|
||||
* Callback to {@link #onBackAnimationFinished} when it is finished or timeout.
|
||||
*/
|
||||
private void startPostCommitAnimation() {
|
||||
if (mPostCommitAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startPostCommitAnimation()");
|
||||
mPostCommitAnimationInProgress = true;
|
||||
mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
|
||||
|
||||
// The next callback should be {@link #onBackAnimationFinished}.
|
||||
if (mTriggerBack) {
|
||||
dispatchOnBackInvoked(mActiveCallback);
|
||||
} else {
|
||||
dispatchOnBackCancelled(mActiveCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the post commit animation is completed or timeout.
|
||||
* This will trigger the real {@link IOnBackInvokedCallback} behavior.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void onBackAnimationFinished() {
|
||||
if (!mPostCommitAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
// Stop timeout runner.
|
||||
mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
|
||||
mPostCommitAnimationInProgress = false;
|
||||
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
|
||||
|
||||
// Trigger the real back.
|
||||
if (mBackNavigationInfo != null) {
|
||||
IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
if (mTriggerBack) {
|
||||
dispatchOnBackInvoked(callback);
|
||||
} else {
|
||||
dispatchOnBackCancelled(callback);
|
||||
}
|
||||
}
|
||||
|
||||
finishBackNavigation();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called after the whole back navigation is completed.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void finishBackNavigation() {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
|
||||
@@ -571,10 +573,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
mTriggerBack = false;
|
||||
mShouldStartOnNextMoveEvent = false;
|
||||
mTouchTracker.reset();
|
||||
mActiveCallback = null;
|
||||
if (backNavigationInfo == null) {
|
||||
return;
|
||||
}
|
||||
stopTransition();
|
||||
if (mBackAnimationFinishedCallback != null) {
|
||||
try {
|
||||
mBackAnimationFinishedCallback.onAnimationFinished(triggerBack);
|
||||
@@ -586,19 +588,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
backNavigationInfo.onBackNavigationFinished(triggerBack);
|
||||
}
|
||||
|
||||
private void startTransition() {
|
||||
if (mTransitionInProgress) {
|
||||
return;
|
||||
}
|
||||
mTransitionInProgress = true;
|
||||
mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
|
||||
}
|
||||
|
||||
void stopTransition() {
|
||||
mShellExecutor.removeCallbacks(mResetTransitionRunnable);
|
||||
mTransitionInProgress = false;
|
||||
}
|
||||
|
||||
private void createAdapter() {
|
||||
IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
|
||||
@Override
|
||||
@@ -624,20 +613,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()");
|
||||
runner.startAnimation(apps, wallpapers, nonApps,
|
||||
BackAnimationController.this::onBackAnimationFinished);
|
||||
|
||||
if (apps.length >= 1) {
|
||||
final int backType = mBackNavigationInfo.getType();
|
||||
IOnBackInvokedCallback targetCallback = mAnimationDefinition.get(backType)
|
||||
.getCallback();
|
||||
dispatchOnBackStarted(
|
||||
targetCallback, mTouchTracker.createStartEvent(apps[0]));
|
||||
mActiveCallback, mTouchTracker.createStartEvent(apps[0]));
|
||||
}
|
||||
|
||||
if (!mBackGestureStarted) {
|
||||
// if the down -> up gesture happened before animation start, we have to
|
||||
// trigger the uninterruptible transition to finish the back animation.
|
||||
final BackEvent backFinish = mTouchTracker.createProgressEvent(1);
|
||||
startTransition();
|
||||
runner.consumeIfGestureFinished(backFinish);
|
||||
final BackEvent backFinish = mTouchTracker.createProgressEvent();
|
||||
dispatchOnBackProgressed(mActiveCallback, backFinish);
|
||||
startPostCommitAnimation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ package com.android.wm.shell.back;
|
||||
|
||||
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.window.BackEvent;
|
||||
import android.window.IBackAnimationRunner;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
@@ -38,11 +38,11 @@ class BackAnimationRunner {
|
||||
private final IOnBackInvokedCallback mCallback;
|
||||
private final IRemoteAnimationRunner mRunner;
|
||||
|
||||
private boolean mTriggerBack;
|
||||
// Whether we are waiting to receive onAnimationStart
|
||||
private boolean mWaitingAnimation;
|
||||
|
||||
BackAnimationRunner(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner) {
|
||||
BackAnimationRunner(@NonNull IOnBackInvokedCallback callback,
|
||||
@NonNull IRemoteAnimationRunner runner) {
|
||||
mCallback = callback;
|
||||
mRunner = runner;
|
||||
}
|
||||
@@ -83,25 +83,7 @@ class BackAnimationRunner {
|
||||
mWaitingAnimation = true;
|
||||
}
|
||||
|
||||
boolean onGestureFinished(boolean triggerBack) {
|
||||
if (mWaitingAnimation) {
|
||||
mTriggerBack = triggerBack;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void consumeIfGestureFinished(final BackEvent backFinish) {
|
||||
Log.d(TAG, "Start transition due to gesture is finished");
|
||||
try {
|
||||
mCallback.onBackProgressed(backFinish);
|
||||
if (mTriggerBack) {
|
||||
mCallback.onBackInvoked();
|
||||
} else {
|
||||
mCallback.onBackCancelled();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "dispatch error: ", e);
|
||||
}
|
||||
boolean isWaitingAnimation() {
|
||||
return mWaitingAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -93,7 +94,10 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
private IActivityTaskManager mActivityTaskManager;
|
||||
|
||||
@Mock
|
||||
private IOnBackInvokedCallback mIOnBackInvokedCallback;
|
||||
private IOnBackInvokedCallback mAppCallback;
|
||||
|
||||
@Mock
|
||||
private IOnBackInvokedCallback mAnimatorCallback;
|
||||
|
||||
@Mock
|
||||
private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
|
||||
@@ -105,8 +109,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
private ShellController mShellController;
|
||||
|
||||
private BackAnimationController mController;
|
||||
|
||||
private int mEventTime = 0;
|
||||
private TestableContentResolver mContentResolver;
|
||||
private TestableLooper mTestableLooper;
|
||||
|
||||
@@ -126,16 +128,15 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
mContentResolver);
|
||||
mController.setEnableUAnimation(true);
|
||||
mShellInit.init();
|
||||
mEventTime = 0;
|
||||
mShellExecutor.flushAll();
|
||||
}
|
||||
|
||||
private void createNavigationInfo(int backType, IOnBackInvokedCallback onBackInvokedCallback) {
|
||||
private void createNavigationInfo(int backType, boolean enableAnimation) {
|
||||
BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
|
||||
.setType(backType)
|
||||
.setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
|
||||
.setOnBackInvokedCallback(onBackInvokedCallback)
|
||||
.setPrepareRemoteAnimation(true);
|
||||
.setOnBackInvokedCallback(mAppCallback)
|
||||
.setPrepareRemoteAnimation(enableAnimation);
|
||||
|
||||
createNavigationInfo(builder);
|
||||
}
|
||||
@@ -176,26 +177,47 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyAnimationFinishes() {
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
boolean[] backNavigationDone = new boolean[]{false};
|
||||
boolean[] triggerBack = new boolean[]{false};
|
||||
createNavigationInfo(new BackNavigationInfo.Builder()
|
||||
.setType(BackNavigationInfo.TYPE_CROSS_ACTIVITY)
|
||||
.setOnBackNavigationDone(
|
||||
new RemoteCallback(result -> {
|
||||
backNavigationDone[0] = true;
|
||||
triggerBack[0] = result.getBoolean(KEY_TRIGGER_BACK);
|
||||
})));
|
||||
triggerBackGesture();
|
||||
assertTrue("Navigation Done callback not called", backNavigationDone[0]);
|
||||
assertTrue("TriggerBack should have been true", triggerBack[0]);
|
||||
public void verifyNavigationFinishes() throws RemoteException {
|
||||
final int[] testTypes = new int[] {BackNavigationInfo.TYPE_RETURN_TO_HOME,
|
||||
BackNavigationInfo.TYPE_CROSS_TASK,
|
||||
BackNavigationInfo.TYPE_CROSS_ACTIVITY,
|
||||
BackNavigationInfo.TYPE_DIALOG_CLOSE,
|
||||
BackNavigationInfo.TYPE_CALLBACK };
|
||||
|
||||
for (int type: testTypes) {
|
||||
registerAnimation(type);
|
||||
}
|
||||
|
||||
for (int type: testTypes) {
|
||||
boolean[] backNavigationDone = new boolean[]{false};
|
||||
boolean[] triggerBack = new boolean[]{false};
|
||||
|
||||
createNavigationInfo(new BackNavigationInfo.Builder()
|
||||
.setType(type)
|
||||
.setOnBackInvokedCallback(mAppCallback)
|
||||
.setPrepareRemoteAnimation(true)
|
||||
.setOnBackNavigationDone(
|
||||
new RemoteCallback(result -> {
|
||||
backNavigationDone[0] = true;
|
||||
triggerBack[0] = result.getBoolean(KEY_TRIGGER_BACK);
|
||||
})));
|
||||
triggerBackGesture();
|
||||
simulateRemoteAnimationStart(type);
|
||||
simulateRemoteAnimationFinished();
|
||||
mShellExecutor.flushAll();
|
||||
|
||||
assertTrue("Navigation Done callback not called for "
|
||||
+ BackNavigationInfo.typeToString(type), backNavigationDone[0]);
|
||||
assertTrue("TriggerBack should have been true", triggerBack[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void backToHome_dispatchesEvents() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, mIOnBackInvokedCallback);
|
||||
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
|
||||
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
|
||||
@@ -203,14 +225,16 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||
|
||||
verify(mAnimatorCallback).onBackStarted(any(BackEvent.class));
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(any(BackEvent.class));
|
||||
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
|
||||
verify(mAnimatorCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture());
|
||||
|
||||
// Check that back invocation is dispatched.
|
||||
mController.setTriggerBack(true); // Fake trigger back
|
||||
doMotionEvent(MotionEvent.ACTION_UP, 0);
|
||||
verify(mIOnBackInvokedCallback).onBackInvoked();
|
||||
verify(mAnimatorCallback).onBackInvoked();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,97 +247,94 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
mActivityTaskManager, mContext,
|
||||
mContentResolver);
|
||||
shellInit.init();
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
|
||||
IOnBackInvokedCallback appCallback = mock(IOnBackInvokedCallback.class);
|
||||
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
|
||||
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false);
|
||||
|
||||
triggerBackGesture();
|
||||
|
||||
verify(appCallback, never()).onBackStarted(any(BackEvent.class));
|
||||
verify(appCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||
verify(appCallback, times(1)).onBackInvoked();
|
||||
verify(mAppCallback, never()).onBackStarted(any());
|
||||
verify(mAppCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||
verify(mAppCallback, times(1)).onBackInvoked();
|
||||
|
||||
verify(mIOnBackInvokedCallback, never()).onBackStarted(any(BackEvent.class));
|
||||
verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
|
||||
verify(mAnimatorCallback, never()).onBackStarted(any());
|
||||
verify(mAnimatorCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||
verify(mAnimatorCallback, never()).onBackInvoked();
|
||||
verify(mBackAnimationRunner, never()).onAnimationStart(
|
||||
anyInt(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoresGesture_transitionInProgress() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
|
||||
|
||||
triggerBackGesture();
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
// Check that back invocation is dispatched.
|
||||
verify(mIOnBackInvokedCallback).onBackInvoked();
|
||||
verify(mAnimatorCallback).onBackInvoked();
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
|
||||
reset(mIOnBackInvokedCallback);
|
||||
reset(mAnimatorCallback);
|
||||
reset(mBackAnimationRunner);
|
||||
|
||||
// Verify that we prevent animation from restarting if another gestures happens before
|
||||
// the previous transition is finished.
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
verifyNoMoreInteractions(mIOnBackInvokedCallback);
|
||||
mController.onBackAnimationFinished();
|
||||
// Pretend the transition handler called finishAnimation.
|
||||
mController.finishBackNavigation();
|
||||
verifyNoMoreInteractions(mAnimatorCallback);
|
||||
|
||||
// Finish back navigation.
|
||||
simulateRemoteAnimationFinished();
|
||||
|
||||
// Verify that more events from a rejected swipe cannot start animation.
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
doMotionEvent(MotionEvent.ACTION_UP, 0);
|
||||
verifyNoMoreInteractions(mIOnBackInvokedCallback);
|
||||
verifyNoMoreInteractions(mAnimatorCallback);
|
||||
|
||||
// Verify that we start accepting gestures again once transition finishes.
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||
verify(mAnimatorCallback).onBackStarted(any());
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptsGesture_transitionTimeout() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
|
||||
|
||||
// In case it is still running in animation.
|
||||
doNothing().when(mAnimatorCallback).onBackInvoked();
|
||||
|
||||
triggerBackGesture();
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
|
||||
reset(mIOnBackInvokedCallback);
|
||||
|
||||
// Simulate transition timeout.
|
||||
mShellExecutor.flushAll();
|
||||
mController.onBackAnimationFinished();
|
||||
// Pretend the transition handler called finishAnimation.
|
||||
mController.finishBackNavigation();
|
||||
reset(mAnimatorCallback);
|
||||
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||
verify(mAnimatorCallback).onBackStarted(any());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void cancelBackInvokeWhenLostFocus() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true);
|
||||
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
// Check that back start and progress is dispatched when first move.
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
|
||||
verify(mAnimatorCallback).onBackStarted(any());
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
|
||||
// Check that back invocation is dispatched.
|
||||
@@ -323,11 +344,11 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
IBinder token = mock(IBinder.class);
|
||||
mController.mFocusObserver.focusLost(token);
|
||||
mShellExecutor.flushAll();
|
||||
verify(mIOnBackInvokedCallback).onBackCancelled();
|
||||
verify(mAnimatorCallback).onBackCancelled();
|
||||
|
||||
// No more back invoke.
|
||||
doMotionEvent(MotionEvent.ACTION_UP, 0);
|
||||
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
|
||||
verify(mAnimatorCallback, never()).onBackInvoked();
|
||||
}
|
||||
|
||||
private void doMotionEvent(int actionDown, int coordinate) {
|
||||
@@ -335,7 +356,6 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
coordinate, coordinate,
|
||||
actionDown,
|
||||
BackEvent.EDGE_LEFT);
|
||||
mEventTime += 10;
|
||||
}
|
||||
|
||||
private void simulateRemoteAnimationStart(int type) throws RemoteException {
|
||||
@@ -347,4 +367,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
mShellExecutor.flushAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void simulateRemoteAnimationFinished() {
|
||||
mController.onBackAnimationFinished();
|
||||
mController.finishBackNavigation();
|
||||
}
|
||||
|
||||
private void registerAnimation(int type) {
|
||||
mController.registerAnimation(type,
|
||||
new BackAnimationRunner(mAnimatorCallback, mBackAnimationRunner));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user