diff options
Diffstat (limited to 'src/com/android/launcher3/touch/AbstractStateChangeTouchController.java')
-rw-r--r-- | src/com/android/launcher3/touch/AbstractStateChangeTouchController.java | 127 |
1 files changed, 113 insertions, 14 deletions
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index bad4976e4..977572ea4 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -18,16 +18,26 @@ package com.android.launcher3.touch; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherStateManager.ANIM_ALL; +import static com.android.launcher3.LauncherStateManager.ATOMIC_COMPONENT; +import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.view.MotionEvent; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager.AnimationComponents; +import com.android.launcher3.LauncherStateManager.AnimationConfig; +import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -41,11 +51,17 @@ public abstract class AbstractStateChangeTouchController implements TouchController, SwipeDetector.Listener { private static final String TAG = "ASCTouchController"; - public static final float RECATCH_REJECTION_FRACTION = .0875f; // Progress after which the transition is assumed to be a success in case user does not fling public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; + /** + * Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this. + */ + public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 0.5f; + private static final long ATOMIC_NORMAL_TO_OVERVIEW_DURATION = 120; + private static final long ATOMIC_OVERVIEW_TO_NORMAL_DURATION = 200; + protected final Launcher mLauncher; protected final SwipeDetector mDetector; @@ -62,6 +78,11 @@ public abstract class AbstractStateChangeTouchController private float mProgressMultiplier; private float mDisplacementShift; + private AnimatorSet mAtomicAnim; + private boolean mPassedOverviewAtomicThreshold; + private boolean mCanBlockFling; + private boolean mBlockFling; + public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) { mLauncher = l; mDetector = new SwipeDetector(l, this, dir); @@ -83,14 +104,8 @@ public abstract class AbstractStateChangeTouchController boolean ignoreSlopWhenSettling = false; if (mCurrentAnimation != null) { - if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) { - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) { - directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; - } else { - directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; - ignoreSlopWhenSettling = true; - } + directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; + ignoreSlopWhenSettling = true; } else { directionsToDetectScroll = getSwipeDirection(); if (directionsToDetectScroll == 0) { @@ -138,7 +153,7 @@ public abstract class AbstractStateChangeTouchController protected abstract LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive); - protected abstract float initCurrentAnimation(); + protected abstract float initCurrentAnimation(@AnimationComponents int animComponents); /** * Returns the container that the touch started from when leaving NORMAL state. @@ -169,14 +184,28 @@ public abstract class AbstractStateChangeTouchController mToState = newToState; mStartProgress = 0; + mPassedOverviewAtomicThreshold = false; + if (mAtomicAnim != null) { + // Most likely the animation is finished by now, but just in case it's not, + // make sure the next state animation starts from the expected state. + mAtomicAnim.end(); + } if (mCurrentAnimation != null) { mCurrentAnimation.setOnCancelRunnable(null); } - mProgressMultiplier = initCurrentAnimation(); + int animComponents = goingBetweenNormalAndOverview(mFromState, mToState) + ? NON_ATOMIC_COMPONENT : ANIM_ALL; + mProgressMultiplier = initCurrentAnimation(animComponents); mCurrentAnimation.dispatchOnStart(); return true; } + private boolean goingBetweenNormalAndOverview(LauncherState fromState, LauncherState toState) { + return (fromState == NORMAL || fromState == OVERVIEW) + && (toState == NORMAL || toState == OVERVIEW) + && mPendingAnimation == null; + } + @Override public void onDragStart(boolean start) { if (mCurrentAnimation == null) { @@ -187,6 +216,8 @@ public abstract class AbstractStateChangeTouchController mCurrentAnimation.pause(); mStartProgress = mCurrentAnimation.getProgressFraction(); } + mCanBlockFling = mFromState == NORMAL; + mBlockFling = false; } @Override @@ -198,17 +229,61 @@ public abstract class AbstractStateChangeTouchController if (progress <= 0) { if (reinitCurrentAnimation(false, isDragTowardPositive)) { mDisplacementShift = displacement; + mBlockFling = mCanBlockFling; } } else if (progress >= 1) { if (reinitCurrentAnimation(true, isDragTowardPositive)) { mDisplacementShift = displacement; + mBlockFling = mCanBlockFling; } + } else if (Math.abs(velocity) < SwipeDetector.RELEASE_VELOCITY_PX_MS) { + // We prevent flinging after passing a state, but allow it if the user pauses briefly. + mBlockFling = false; } + return true; } protected void updateProgress(float fraction) { mCurrentAnimation.setPlayFraction(fraction); + maybeUpdateAtomicAnim(mFromState, mToState, fraction); + } + + /** + * When going between normal and overview states, see if we passed the overview threshold and + * play the appropriate atomic animation if so. + */ + private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState, + float progress) { + if (!goingBetweenNormalAndOverview(fromState, toState)) { + return; + } + float threshold = toState == OVERVIEW ? ATOMIC_OVERVIEW_ANIM_THRESHOLD + : 1f - ATOMIC_OVERVIEW_ANIM_THRESHOLD; + boolean passedThreshold = progress >= threshold; + if (passedThreshold != mPassedOverviewAtomicThreshold) { + LauncherState targetState = passedThreshold ? toState : fromState; + mPassedOverviewAtomicThreshold = passedThreshold; + if (mAtomicAnim != null) { + mAtomicAnim.cancel(); + } + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + AnimationConfig config = new AnimationConfig(); + config.animComponents = ATOMIC_COMPONENT; + config.duration = targetState == OVERVIEW ? ATOMIC_NORMAL_TO_OVERVIEW_DURATION + : ATOMIC_OVERVIEW_TO_NORMAL_DURATION; + for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) { + handler.setStateWithAnimation(targetState, builder, config); + } + mAtomicAnim = builder.build(); + mAtomicAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAtomicAnim = null; + } + }); + mAtomicAnim.start(); + } } @Override @@ -217,6 +292,11 @@ public abstract class AbstractStateChangeTouchController final LauncherState targetState; final float progress = mCurrentAnimation.getProgressFraction(); + boolean blockedFling = fling && mBlockFling; + if (blockedFling) { + fling = false; + } + if (fling) { logAction = Touch.FLING; targetState = @@ -231,6 +311,8 @@ public abstract class AbstractStateChangeTouchController final float endProgress; final float startProgress; final long duration; + // Increase the duration if we prevented the fling, as we are going against a high velocity. + final long durationMultiplier = blockedFling && targetState == mFromState ? 6 : 1; if (targetState == mToState) { endProgress = 1; @@ -241,7 +323,7 @@ public abstract class AbstractStateChangeTouchController startProgress = Utilities.boundToRange( progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f); duration = SwipeDetector.calculateDuration(velocity, - endProgress - Math.max(progress, 0)); + endProgress - Math.max(progress, 0)) * durationMultiplier; } } else { // Let the state manager know that the animation didn't go to the target state, @@ -259,18 +341,35 @@ public abstract class AbstractStateChangeTouchController startProgress = Utilities.boundToRange( progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f); duration = SwipeDetector.calculateDuration(velocity, - Math.min(progress, 1) - endProgress); + Math.min(progress, 1) - endProgress) * durationMultiplier; } } mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction)); ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); anim.setFloatValues(startProgress, endProgress); - updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling); + maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f); + updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()), + targetState, velocity, fling); mCurrentAnimation.dispatchOnStart(); anim.start(); } + private long getRemainingAtomicDuration() { + if (mAtomicAnim == null) { + return 0; + } + if (Utilities.ATLEAST_OREO) { + return mAtomicAnim.getTotalDuration() - mAtomicAnim.getCurrentPlayTime(); + } else { + long remainingDuration = 0; + for (Animator anim : mAtomicAnim.getChildAnimations()) { + remainingDuration = Math.max(remainingDuration, anim.getDuration()); + } + return remainingDuration; + } + } + protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration, LauncherState targetState, float velocity, boolean isFling) { animator.setDuration(expectedDuration) |