Loading libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +97 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; Loading @@ -37,7 +40,9 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.util.SparseArray; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; Loading @@ -56,6 +61,7 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; Loading @@ -80,6 +86,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public static boolean IS_U_ANIMATION_ENABLED = SystemProperties.getInt("persist.wm.debug.predictive_back_anim", SETTING_VALUE_ON) == SETTING_VALUE_ON; public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms public static final float FLING_SPEED_UP_FACTOR = 0.6f; /** * The maximum additional progress in case of fling gesture. * The end animation starts after the user lifts the finger from the screen, we continue to * fire {@link BackEvent}s until the velocity reaches 0. */ private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */ /** Predictive back animation developer option */ private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** Loading @@ -96,6 +113,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mShouldStartOnNextMoveEvent = false; /** @see #setTriggerBack(boolean) */ private boolean mTriggerBack; private FlingAnimationUtils mFlingAnimationUtils; @Nullable private BackNavigationInfo mBackNavigationInfo; Loading Loading @@ -174,6 +192,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics) .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); } @VisibleForTesting Loading Loading @@ -465,6 +488,78 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } /** * Allows us to manage the fling gesture, it smoothly animates the current progress value to * the final position, calculated based on the current velocity. * * @param callback the callback to be invoked when the animation ends. */ private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback) { if (callback == null) { return; } boolean animationStarted = false; if (mBackNavigationInfo != null && mBackNavigationInfo.isAnimationCallback()) { final BackMotionEvent backMotionEvent = mTouchTracker.createProgressEvent(); if (backMotionEvent != null) { // Constraints - absolute values float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond(); float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond(); float maxX = mTouchTracker.getMaxX(); // px float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px // Current state float currentX = backMotionEvent.getTouchX(); float velocity = MathUtils.constrain(backMotionEvent.getVelocityX(), -maxVelocity, maxVelocity); // Target state float animationFaction = velocity / maxVelocity; // value between -1 and 1 float flingDistance = animationFaction * maxFlingDistance; // px float endX = MathUtils.constrain(currentX + flingDistance, 0f, maxX); if (!Float.isNaN(endX) && currentX != endX && Math.abs(velocity) >= minVelocity) { ValueAnimator animator = ValueAnimator.ofFloat(currentX, endX); mFlingAnimationUtils.apply( /* animator = */ animator, /* currValue = */ currentX, /* endValue = */ endX, /* velocity = */ velocity, /* maxDistance = */ maxFlingDistance ); animator.addUpdateListener(animation -> { Float animatedValue = (Float) animation.getAnimatedValue(); float progress = mTouchTracker.getProgress(animatedValue); final BackMotionEvent backEvent = mTouchTracker .createProgressEvent(progress); dispatchOnBackProgressed(mActiveCallback, backEvent); }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { dispatchOnBackInvoked(callback); } }); animator.start(); animationStarted = true; } } } if (!animationStarted) { dispatchOnBackInvoked(callback); } } private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) { if (callback == null) { return; Loading Loading @@ -530,7 +625,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mBackNavigationInfo != null) { final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); if (mTriggerBack) { dispatchOnBackInvoked(callback); dispatchOrAnimateOnBackInvoked(callback); } else { dispatchOnBackCancelled(callback); } Loading Loading @@ -605,7 +700,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont // The next callback should be {@link #onBackAnimationFinished}. if (mTriggerBack) { dispatchOnBackInvoked(mActiveCallback); dispatchOrAnimateOnBackInvoked(mActiveCallback); } else { dispatchOnBackCancelled(mActiveCallback); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +32 −15 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package com.android.wm.shell.back; import android.annotation.FloatRange; import android.os.SystemProperties; import android.util.MathUtils; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.window.BackEvent; import android.window.BackMotionEvent; Loading Loading @@ -99,26 +102,40 @@ class TouchTracker { } BackMotionEvent 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) { progress = getProgress(mLatestTouchX); } return createProgressEvent(progress); } /** * Progress value computed from the touch position. * * @param touchX the X touch position of the {@link MotionEvent}. * @return progress value */ @FloatRange(from = 0.0, to = 1.0) float getProgress(float touchX) { // 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); float deltaX = Math.abs(startX - touchX); float maxX = getMaxX(); maxX = maxX == 0 ? 1 : maxX; return MathUtils.constrain(deltaX / maxX, 0, 1); } return createProgressEvent(progress); /** * Maximum X value (in pixels). * Progress is considered to be completed (1f) when this limit is exceeded. */ float getMaxX() { return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold; } BackMotionEvent createProgressEvent(float progress) { Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +55 −10 Original line number Diff line number Diff line Loading @@ -135,12 +135,15 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellExecutor.flushAll(); } private void createNavigationInfo(int backType, boolean enableAnimation) { private void createNavigationInfo(int backType, boolean enableAnimation, boolean isAnimationCallback) { BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setType(backType) .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(enableAnimation); .setPrepareRemoteAnimation(enableAnimation) .setAnimationCallback(isAnimationCallback); createNavigationInfo(builder); } Loading Loading @@ -218,7 +221,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void backToHome_dispatchesEvents() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); Loading @@ -239,6 +244,32 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mAnimatorCallback).onBackInvoked(); } @Test public void backToHomeWithAnimationCallback_dispatchesEvents() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ true); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class)); verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); ArgumentCaptor<BackMotionEvent> backEventCaptor = ArgumentCaptor.forClass(BackMotionEvent.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(mAnimatorCallback).onBackInvoked(); } @Test public void animationDisabledFromSettings() throws RemoteException { // Toggle the setting off Loading @@ -254,7 +285,9 @@ public class BackAnimationControllerTest extends ShellTestCase { ArgumentCaptor<BackMotionEvent> backEventCaptor = ArgumentCaptor.forClass(BackMotionEvent.class); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ false, /* isAnimationCallback = */ false); triggerBackGesture(); releaseBackGesture(); Loading @@ -271,7 +304,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void ignoresGesture_transitionInProgress() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); triggerBackGesture(); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); Loading Loading @@ -309,7 +344,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void acceptsGesture_transitionTimeout() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); // In case it is still running in animation. doNothing().when(mAnimatorCallback).onBackInvoked(); Loading @@ -334,7 +371,9 @@ public class BackAnimationControllerTest extends ShellTestCase { public void cancelBackInvokeWhenLostFocus() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. Loading Loading @@ -454,7 +493,9 @@ public class BackAnimationControllerTest extends ShellTestCase { mController.registerAnimation(type, animationRunner); createNavigationInfo(type, true); createNavigationInfo(type, /* enableAnimation = */ true, /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); Loading @@ -473,11 +514,15 @@ public class BackAnimationControllerTest extends ShellTestCase { } private void doMotionEvent(int actionDown, int coordinate) { doMotionEvent(actionDown, coordinate, 0); } private void doMotionEvent(int actionDown, int coordinate, float velocity) { mController.onMotionEvent( /* touchX */ coordinate, /* touchY */ coordinate, /* velocityX = */ 0, /* velocityY = */ 0, /* velocityX = */ velocity, /* velocityY = */ velocity, /* keyAction */ actionDown, /* swipeEdge */ BackEvent.EDGE_LEFT); } Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +97 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityTaskManager; Loading @@ -37,7 +40,9 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.util.SparseArray; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; Loading @@ -56,6 +61,7 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; Loading @@ -80,6 +86,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public static boolean IS_U_ANIMATION_ENABLED = SystemProperties.getInt("persist.wm.debug.predictive_back_anim", SETTING_VALUE_ON) == SETTING_VALUE_ON; public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms public static final float FLING_SPEED_UP_FACTOR = 0.6f; /** * The maximum additional progress in case of fling gesture. * The end animation starts after the user lifts the finger from the screen, we continue to * fire {@link BackEvent}s until the velocity reaches 0. */ private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */ /** Predictive back animation developer option */ private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** Loading @@ -96,6 +113,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mShouldStartOnNextMoveEvent = false; /** @see #setTriggerBack(boolean) */ private boolean mTriggerBack; private FlingAnimationUtils mFlingAnimationUtils; @Nullable private BackNavigationInfo mBackNavigationInfo; Loading Loading @@ -174,6 +192,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics) .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); } @VisibleForTesting Loading Loading @@ -465,6 +488,78 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } /** * Allows us to manage the fling gesture, it smoothly animates the current progress value to * the final position, calculated based on the current velocity. * * @param callback the callback to be invoked when the animation ends. */ private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback) { if (callback == null) { return; } boolean animationStarted = false; if (mBackNavigationInfo != null && mBackNavigationInfo.isAnimationCallback()) { final BackMotionEvent backMotionEvent = mTouchTracker.createProgressEvent(); if (backMotionEvent != null) { // Constraints - absolute values float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond(); float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond(); float maxX = mTouchTracker.getMaxX(); // px float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px // Current state float currentX = backMotionEvent.getTouchX(); float velocity = MathUtils.constrain(backMotionEvent.getVelocityX(), -maxVelocity, maxVelocity); // Target state float animationFaction = velocity / maxVelocity; // value between -1 and 1 float flingDistance = animationFaction * maxFlingDistance; // px float endX = MathUtils.constrain(currentX + flingDistance, 0f, maxX); if (!Float.isNaN(endX) && currentX != endX && Math.abs(velocity) >= minVelocity) { ValueAnimator animator = ValueAnimator.ofFloat(currentX, endX); mFlingAnimationUtils.apply( /* animator = */ animator, /* currValue = */ currentX, /* endValue = */ endX, /* velocity = */ velocity, /* maxDistance = */ maxFlingDistance ); animator.addUpdateListener(animation -> { Float animatedValue = (Float) animation.getAnimatedValue(); float progress = mTouchTracker.getProgress(animatedValue); final BackMotionEvent backEvent = mTouchTracker .createProgressEvent(progress); dispatchOnBackProgressed(mActiveCallback, backEvent); }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { dispatchOnBackInvoked(callback); } }); animator.start(); animationStarted = true; } } } if (!animationStarted) { dispatchOnBackInvoked(callback); } } private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) { if (callback == null) { return; Loading Loading @@ -530,7 +625,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mBackNavigationInfo != null) { final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); if (mTriggerBack) { dispatchOnBackInvoked(callback); dispatchOrAnimateOnBackInvoked(callback); } else { dispatchOnBackCancelled(callback); } Loading Loading @@ -605,7 +700,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont // The next callback should be {@link #onBackAnimationFinished}. if (mTriggerBack) { dispatchOnBackInvoked(mActiveCallback); dispatchOrAnimateOnBackInvoked(mActiveCallback); } else { dispatchOnBackCancelled(mActiveCallback); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +32 −15 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package com.android.wm.shell.back; import android.annotation.FloatRange; import android.os.SystemProperties; import android.util.MathUtils; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.window.BackEvent; import android.window.BackMotionEvent; Loading Loading @@ -99,26 +102,40 @@ class TouchTracker { } BackMotionEvent 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) { progress = getProgress(mLatestTouchX); } return createProgressEvent(progress); } /** * Progress value computed from the touch position. * * @param touchX the X touch position of the {@link MotionEvent}. * @return progress value */ @FloatRange(from = 0.0, to = 1.0) float getProgress(float touchX) { // 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); float deltaX = Math.abs(startX - touchX); float maxX = getMaxX(); maxX = maxX == 0 ? 1 : maxX; return MathUtils.constrain(deltaX / maxX, 0, 1); } return createProgressEvent(progress); /** * Maximum X value (in pixels). * Progress is considered to be completed (1f) when this limit is exceeded. */ float getMaxX() { return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold; } BackMotionEvent createProgressEvent(float progress) { Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +55 −10 Original line number Diff line number Diff line Loading @@ -135,12 +135,15 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellExecutor.flushAll(); } private void createNavigationInfo(int backType, boolean enableAnimation) { private void createNavigationInfo(int backType, boolean enableAnimation, boolean isAnimationCallback) { BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setType(backType) .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(enableAnimation); .setPrepareRemoteAnimation(enableAnimation) .setAnimationCallback(isAnimationCallback); createNavigationInfo(builder); } Loading Loading @@ -218,7 +221,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void backToHome_dispatchesEvents() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); Loading @@ -239,6 +244,32 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mAnimatorCallback).onBackInvoked(); } @Test public void backToHomeWithAnimationCallback_dispatchesEvents() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ true); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); verify(mAnimatorCallback).onBackStarted(any(BackMotionEvent.class)); verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any()); ArgumentCaptor<BackMotionEvent> backEventCaptor = ArgumentCaptor.forClass(BackMotionEvent.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(mAnimatorCallback).onBackInvoked(); } @Test public void animationDisabledFromSettings() throws RemoteException { // Toggle the setting off Loading @@ -254,7 +285,9 @@ public class BackAnimationControllerTest extends ShellTestCase { ArgumentCaptor<BackMotionEvent> backEventCaptor = ArgumentCaptor.forClass(BackMotionEvent.class); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ false, /* isAnimationCallback = */ false); triggerBackGesture(); releaseBackGesture(); Loading @@ -271,7 +304,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void ignoresGesture_transitionInProgress() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); triggerBackGesture(); simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME); Loading Loading @@ -309,7 +344,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void acceptsGesture_transitionTimeout() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); // In case it is still running in animation. doNothing().when(mAnimatorCallback).onBackInvoked(); Loading @@ -334,7 +371,9 @@ public class BackAnimationControllerTest extends ShellTestCase { public void cancelBackInvokeWhenLostFocus() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, true); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, /* enableAnimation = */ true, /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. Loading Loading @@ -454,7 +493,9 @@ public class BackAnimationControllerTest extends ShellTestCase { mController.registerAnimation(type, animationRunner); createNavigationInfo(type, true); createNavigationInfo(type, /* enableAnimation = */ true, /* isAnimationCallback = */ false); doMotionEvent(MotionEvent.ACTION_DOWN, 0); Loading @@ -473,11 +514,15 @@ public class BackAnimationControllerTest extends ShellTestCase { } private void doMotionEvent(int actionDown, int coordinate) { doMotionEvent(actionDown, coordinate, 0); } private void doMotionEvent(int actionDown, int coordinate, float velocity) { mController.onMotionEvent( /* touchX */ coordinate, /* touchY */ coordinate, /* velocityX = */ 0, /* velocityY = */ 0, /* velocityX = */ velocity, /* velocityY = */ velocity, /* keyAction */ actionDown, /* swipeEdge */ BackEvent.EDGE_LEFT); } Loading