Don't end atomic animation when passing through state
Previously we were ending the atomic animation with the assumption that it should be complete/almost complete by the time you drag to the next state. However, it is very easy to drag quickly enough where that assumption doesn't hold, and thus you just see the atomic animation pop to the end (i.e. recents showing without animation). Now instead of ending the atomic animation, we let it continue. But because the new state animation will have an atomic component that interferes with the still playing atomic animation, we have to control the atomic component separately; we control the non-atomic components until the atomic animation ends, at which point we create a separate controller to control the atomic components. Bug: 76449024 Change-Id: Ia4bf19e26d0838f952d9e500fbdd8aba19856a41
This commit is contained in:
parent
6becf7c07d
commit
fc564f1477
|
@ -28,6 +28,7 @@ import android.animation.Animator;
|
|||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
|
@ -36,6 +37,7 @@ 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.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
|
@ -77,11 +79,18 @@ public abstract class AbstractStateChangeTouchController
|
|||
// Ratio of transition process [0, 1] to drag displacement (px)
|
||||
private float mProgressMultiplier;
|
||||
private float mDisplacementShift;
|
||||
private boolean mCanBlockFling;
|
||||
private boolean mBlockFling;
|
||||
|
||||
private AnimatorSet mAtomicAnim;
|
||||
private boolean mPassedOverviewAtomicThreshold;
|
||||
private boolean mCanBlockFling;
|
||||
private boolean mBlockFling;
|
||||
// mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
|
||||
// However, if we reinit to transition to a new state (e.g. OVERVIEW -> ALL_APPS) before the
|
||||
// atomic animation finishes, we only control the non-atomic components so that we don't
|
||||
// interfere with the atomic animation. When the atomic animation ends, we start controlling
|
||||
// the atomic components as well, using this controller.
|
||||
private AnimatorPlaybackController mAtomicComponentsController;
|
||||
private float mAtomicComponentsStartProgress;
|
||||
|
||||
public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
|
||||
mLauncher = l;
|
||||
|
@ -185,16 +194,30 @@ public abstract class AbstractStateChangeTouchController
|
|||
|
||||
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);
|
||||
}
|
||||
int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
|
||||
? NON_ATOMIC_COMPONENT : ANIM_ALL;
|
||||
if (mAtomicAnim != null) {
|
||||
// Control the non-atomic components until the atomic animation finishes, then control
|
||||
// the atomic components as well.
|
||||
animComponents = NON_ATOMIC_COMPONENT;
|
||||
mAtomicAnim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animation) {
|
||||
cancelAtomicComponentsController();
|
||||
mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
|
||||
long duration = (long) (getShiftRange() * 2);
|
||||
mAtomicComponentsController = AnimatorPlaybackController.wrap(
|
||||
createAtomicAnimForState(mToState, duration), duration);
|
||||
mAtomicComponentsController.dispatchOnStart();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (goingBetweenNormalAndOverview(mFromState, mToState)) {
|
||||
cancelAtomicComponentsController();
|
||||
}
|
||||
mProgressMultiplier = initCurrentAnimation(animComponents);
|
||||
mCurrentAnimation.dispatchOnStart();
|
||||
return true;
|
||||
|
@ -210,6 +233,7 @@ public abstract class AbstractStateChangeTouchController
|
|||
public void onDragStart(boolean start) {
|
||||
if (mCurrentAnimation == null) {
|
||||
mFromState = mToState = null;
|
||||
mAtomicComponentsController = null;
|
||||
reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());
|
||||
mDisplacementShift = 0;
|
||||
} else {
|
||||
|
@ -246,6 +270,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
|
||||
protected void updateProgress(float fraction) {
|
||||
mCurrentAnimation.setPlayFraction(fraction);
|
||||
if (mAtomicComponentsController != null) {
|
||||
mAtomicComponentsController.setPlayFraction(fraction - mAtomicComponentsStartProgress);
|
||||
}
|
||||
maybeUpdateAtomicAnim(mFromState, mToState, fraction);
|
||||
}
|
||||
|
||||
|
@ -267,15 +294,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
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
|
||||
long 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 = createAtomicAnimForState(targetState, duration);
|
||||
mAtomicAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
|
@ -283,9 +304,21 @@ public abstract class AbstractStateChangeTouchController
|
|||
}
|
||||
});
|
||||
mAtomicAnim.start();
|
||||
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
}
|
||||
}
|
||||
|
||||
private AnimatorSet createAtomicAnimForState(LauncherState targetState, long duration) {
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
AnimationConfig config = new AnimationConfig();
|
||||
config.animComponents = ATOMIC_COMPONENT;
|
||||
config.duration = duration;
|
||||
for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
|
||||
handler.setStateWithAnimation(targetState, builder, config);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
final int logAction;
|
||||
|
@ -353,6 +386,38 @@ public abstract class AbstractStateChangeTouchController
|
|||
targetState, velocity, fling);
|
||||
mCurrentAnimation.dispatchOnStart();
|
||||
anim.start();
|
||||
if (mAtomicAnim == null) {
|
||||
startAtomicComponentsAnim(endProgress, anim.getDuration());
|
||||
} else {
|
||||
mAtomicAnim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
startAtomicComponentsAnim(endProgress, anim.getDuration());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the atomic components from the current progress to the final progress.
|
||||
*
|
||||
* Note that this only applies when we are controlling the atomic components separately from
|
||||
* the non-atomic components, which only happens if we reinit before the atomic animation
|
||||
* finishes.
|
||||
*/
|
||||
private void startAtomicComponentsAnim(float toProgress, long duration) {
|
||||
if (mAtomicComponentsController != null) {
|
||||
ValueAnimator atomicAnim = mAtomicComponentsController.getAnimationPlayer();
|
||||
atomicAnim.setFloatValues(mAtomicComponentsController.getProgressFraction(), toProgress);
|
||||
atomicAnim.setDuration(duration);
|
||||
atomicAnim.start();
|
||||
atomicAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mAtomicComponentsController = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private long getRemainingAtomicDuration() {
|
||||
|
@ -409,7 +474,15 @@ public abstract class AbstractStateChangeTouchController
|
|||
|
||||
protected void clearState() {
|
||||
mCurrentAnimation = null;
|
||||
cancelAtomicComponentsController();
|
||||
mDetector.finishedScrolling();
|
||||
mDetector.setDetectableScrollConditions(0, false);
|
||||
}
|
||||
|
||||
private void cancelAtomicComponentsController() {
|
||||
if (mAtomicComponentsController != null) {
|
||||
mAtomicComponentsController.getAnimationPlayer().cancel();
|
||||
mAtomicComponentsController = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue