2017-10-27 11:05:26 -07:00
|
|
|
/*
|
2018-03-14 17:51:49 -07:00
|
|
|
* Copyright (C) 2019 The Android Open Source Project
|
2017-10-27 11:05:26 -07:00
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2018-03-14 17:51:49 -07:00
|
|
|
package com.android.launcher3.touch;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
2018-04-24 13:42:59 -07:00
|
|
|
import static com.android.launcher3.LauncherState.ALL_APPS;
|
|
|
|
|
import static com.android.launcher3.LauncherState.NORMAL;
|
|
|
|
|
import static com.android.launcher3.LauncherState.OVERVIEW;
|
2018-04-19 11:39:34 -07:00
|
|
|
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;
|
2018-04-24 12:21:28 -07:00
|
|
|
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
|
2017-10-27 11:05:26 -07:00
|
|
|
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
|
|
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
import android.animation.Animator;
|
|
|
|
|
import android.animation.AnimatorListenerAdapter;
|
|
|
|
|
import android.animation.AnimatorSet;
|
2017-10-27 11:05:26 -07:00
|
|
|
import android.animation.ValueAnimator;
|
|
|
|
|
import android.view.MotionEvent;
|
|
|
|
|
|
2017-12-12 12:02:29 -08:00
|
|
|
import com.android.launcher3.Launcher;
|
|
|
|
|
import com.android.launcher3.LauncherState;
|
2018-04-19 11:39:34 -07:00
|
|
|
import com.android.launcher3.LauncherStateManager.AnimationComponents;
|
|
|
|
|
import com.android.launcher3.LauncherStateManager.AnimationConfig;
|
|
|
|
|
import com.android.launcher3.LauncherStateManager.StateHandler;
|
2017-12-12 12:02:29 -08:00
|
|
|
import com.android.launcher3.Utilities;
|
2017-10-27 11:05:26 -07:00
|
|
|
import com.android.launcher3.anim.AnimatorPlaybackController;
|
2018-04-19 11:39:34 -07:00
|
|
|
import com.android.launcher3.anim.AnimatorSetBuilder;
|
2018-03-14 17:51:49 -07:00
|
|
|
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
|
|
|
|
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
2018-04-24 13:42:59 -07:00
|
|
|
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
2018-04-02 15:22:06 -07:00
|
|
|
import com.android.launcher3.util.PendingAnimation;
|
2018-04-27 12:33:24 -05:00
|
|
|
import com.android.launcher3.util.TouchController;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
|
|
|
|
/**
|
2018-03-14 17:51:49 -07:00
|
|
|
* TouchController for handling state changes
|
2017-10-27 11:05:26 -07:00
|
|
|
*/
|
2018-04-27 12:33:24 -05:00
|
|
|
public abstract class AbstractStateChangeTouchController
|
2017-10-27 11:05:26 -07:00
|
|
|
implements TouchController, SwipeDetector.Listener {
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
private static final String TAG = "ASCTouchController";
|
2017-10-27 11:05:26 -07:00
|
|
|
|
|
|
|
|
// Progress after which the transition is assumed to be a success in case user does not fling
|
2018-03-14 17:51:49 -07:00
|
|
|
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
|
2017-12-12 12:02:29 -08:00
|
|
|
protected final Launcher mLauncher;
|
2018-02-01 14:46:13 -08:00
|
|
|
protected final SwipeDetector mDetector;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
|
|
|
|
private boolean mNoIntercept;
|
2018-03-14 17:51:49 -07:00
|
|
|
protected int mStartContainerType;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
protected LauncherState mFromState;
|
2018-02-16 03:23:38 +00:00
|
|
|
protected LauncherState mToState;
|
2018-03-14 17:51:49 -07:00
|
|
|
protected AnimatorPlaybackController mCurrentAnimation;
|
2018-04-02 15:22:06 -07:00
|
|
|
protected PendingAnimation mPendingAnimation;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
|
|
|
|
private float mStartProgress;
|
|
|
|
|
// Ratio of transition process [0, 1] to drag displacement (px)
|
|
|
|
|
private float mProgressMultiplier;
|
2018-03-28 11:21:49 -07:00
|
|
|
private float mDisplacementShift;
|
2017-10-27 11:05:26 -07:00
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
private AnimatorSet mAtomicAnim;
|
|
|
|
|
private boolean mPassedOverviewAtomicThreshold;
|
|
|
|
|
private boolean mCanBlockFling;
|
|
|
|
|
private boolean mBlockFling;
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
|
2017-10-27 11:05:26 -07:00
|
|
|
mLauncher = l;
|
2018-01-11 12:42:05 -08:00
|
|
|
mDetector = new SwipeDetector(l, this, dir);
|
2017-10-27 11:05:26 -07:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
protected abstract boolean canInterceptTouch(MotionEvent ev);
|
2017-12-12 12:02:29 -08:00
|
|
|
|
2017-10-27 11:05:26 -07:00
|
|
|
@Override
|
2018-03-14 17:51:49 -07:00
|
|
|
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
2017-10-27 11:05:26 -07:00
|
|
|
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
|
|
|
|
mNoIntercept = !canInterceptTouch(ev);
|
|
|
|
|
if (mNoIntercept) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now figure out which direction scroll events the controller will start
|
|
|
|
|
// calling the callbacks.
|
|
|
|
|
final int directionsToDetectScroll;
|
|
|
|
|
boolean ignoreSlopWhenSettling = false;
|
|
|
|
|
|
|
|
|
|
if (mCurrentAnimation != null) {
|
2018-04-19 11:39:34 -07:00
|
|
|
directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
|
|
|
|
|
ignoreSlopWhenSettling = true;
|
2017-10-27 11:05:26 -07:00
|
|
|
} else {
|
2018-04-24 13:42:59 -07:00
|
|
|
directionsToDetectScroll = getSwipeDirection();
|
2018-03-14 17:51:49 -07:00
|
|
|
if (directionsToDetectScroll == 0) {
|
|
|
|
|
mNoIntercept = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-10-27 11:05:26 -07:00
|
|
|
}
|
|
|
|
|
mDetector.setDetectableScrollConditions(
|
|
|
|
|
directionsToDetectScroll, ignoreSlopWhenSettling);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mNoIntercept) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onControllerTouchEvent(ev);
|
|
|
|
|
return mDetector.isDraggingOrSettling();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-24 13:42:59 -07:00
|
|
|
private int getSwipeDirection() {
|
|
|
|
|
LauncherState fromState = mLauncher.getStateManager().getState();
|
|
|
|
|
int swipeDirection = 0;
|
|
|
|
|
if (getTargetState(fromState, true /* isDragTowardPositive */) != fromState) {
|
|
|
|
|
swipeDirection |= SwipeDetector.DIRECTION_POSITIVE;
|
|
|
|
|
}
|
|
|
|
|
if (getTargetState(fromState, false /* isDragTowardPositive */) != fromState) {
|
|
|
|
|
swipeDirection |= SwipeDetector.DIRECTION_NEGATIVE;
|
|
|
|
|
}
|
|
|
|
|
return swipeDirection;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 11:05:26 -07:00
|
|
|
@Override
|
2018-03-14 17:51:49 -07:00
|
|
|
public final boolean onControllerTouchEvent(MotionEvent ev) {
|
2017-10-27 11:05:26 -07:00
|
|
|
return mDetector.onTouchEvent(ev);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
protected float getShiftRange() {
|
|
|
|
|
return mLauncher.getAllAppsController().getShiftRange();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-06 14:22:46 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the state to go to from fromState given the drag direction. If there is no state in
|
|
|
|
|
* that direction, returns fromState.
|
|
|
|
|
*/
|
2018-03-28 11:21:49 -07:00
|
|
|
protected abstract LauncherState getTargetState(LauncherState fromState,
|
|
|
|
|
boolean isDragTowardPositive);
|
|
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
protected abstract float initCurrentAnimation(@AnimationComponents int animComponents);
|
2018-03-14 17:51:49 -07:00
|
|
|
|
2018-04-24 13:42:59 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the container that the touch started from when leaving NORMAL state.
|
|
|
|
|
*/
|
|
|
|
|
protected abstract int getLogContainerTypeForNormalState();
|
|
|
|
|
|
2018-03-28 11:21:49 -07:00
|
|
|
private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
|
|
|
|
|
LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
|
|
|
|
|
: reachedToState ? mToState : mFromState;
|
|
|
|
|
LauncherState newToState = getTargetState(newFromState, isDragTowardPositive);
|
|
|
|
|
|
|
|
|
|
if (newFromState == mFromState && newToState == mToState || (newFromState == newToState)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-24 13:42:59 -07:00
|
|
|
if (reachedToState) {
|
|
|
|
|
logReachedState(Touch.SWIPE);
|
|
|
|
|
}
|
|
|
|
|
if (newFromState == ALL_APPS) {
|
|
|
|
|
mStartContainerType = ContainerType.ALLAPPS;
|
|
|
|
|
} else if (newFromState == NORMAL) {
|
|
|
|
|
mStartContainerType = getLogContainerTypeForNormalState();
|
|
|
|
|
} else if (newFromState == OVERVIEW){
|
|
|
|
|
mStartContainerType = ContainerType.TASKSWITCHER;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-28 11:21:49 -07:00
|
|
|
mFromState = newFromState;
|
|
|
|
|
mToState = newToState;
|
|
|
|
|
|
|
|
|
|
mStartProgress = 0;
|
2018-04-19 11:39:34 -07:00
|
|
|
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();
|
|
|
|
|
}
|
2018-04-27 12:33:24 -05:00
|
|
|
if (mCurrentAnimation != null) {
|
|
|
|
|
mCurrentAnimation.setOnCancelRunnable(null);
|
|
|
|
|
}
|
2018-04-19 11:39:34 -07:00
|
|
|
int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
|
|
|
|
|
? NON_ATOMIC_COMPONENT : ANIM_ALL;
|
|
|
|
|
mProgressMultiplier = initCurrentAnimation(animComponents);
|
2018-03-28 11:21:49 -07:00
|
|
|
mCurrentAnimation.dispatchOnStart();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
private boolean goingBetweenNormalAndOverview(LauncherState fromState, LauncherState toState) {
|
|
|
|
|
return (fromState == NORMAL || fromState == OVERVIEW)
|
|
|
|
|
&& (toState == NORMAL || toState == OVERVIEW)
|
|
|
|
|
&& mPendingAnimation == null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 11:05:26 -07:00
|
|
|
@Override
|
|
|
|
|
public void onDragStart(boolean start) {
|
|
|
|
|
if (mCurrentAnimation == null) {
|
2018-03-28 11:21:49 -07:00
|
|
|
mFromState = mToState = null;
|
|
|
|
|
reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());
|
|
|
|
|
mDisplacementShift = 0;
|
2017-10-27 11:05:26 -07:00
|
|
|
} else {
|
|
|
|
|
mCurrentAnimation.pause();
|
|
|
|
|
mStartProgress = mCurrentAnimation.getProgressFraction();
|
|
|
|
|
}
|
2018-04-19 11:39:34 -07:00
|
|
|
mCanBlockFling = mFromState == NORMAL;
|
|
|
|
|
mBlockFling = false;
|
2017-10-27 11:05:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onDrag(float displacement, float velocity) {
|
2018-03-28 11:21:49 -07:00
|
|
|
float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
|
|
|
|
|
float progress = deltaProgress + mStartProgress;
|
|
|
|
|
updateProgress(progress);
|
|
|
|
|
boolean isDragTowardPositive = (displacement - mDisplacementShift) < 0;
|
|
|
|
|
if (progress <= 0) {
|
|
|
|
|
if (reinitCurrentAnimation(false, isDragTowardPositive)) {
|
|
|
|
|
mDisplacementShift = displacement;
|
2018-04-19 11:39:34 -07:00
|
|
|
mBlockFling = mCanBlockFling;
|
2018-03-28 11:21:49 -07:00
|
|
|
}
|
|
|
|
|
} else if (progress >= 1) {
|
|
|
|
|
if (reinitCurrentAnimation(true, isDragTowardPositive)) {
|
|
|
|
|
mDisplacementShift = displacement;
|
2018-04-19 11:39:34 -07:00
|
|
|
mBlockFling = mCanBlockFling;
|
2018-03-28 11:21:49 -07:00
|
|
|
}
|
2018-04-19 11:39:34 -07:00
|
|
|
} 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;
|
2018-03-28 11:21:49 -07:00
|
|
|
}
|
2018-04-19 11:39:34 -07:00
|
|
|
|
2017-10-27 11:05:26 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-21 09:32:27 -07:00
|
|
|
protected void updateProgress(float fraction) {
|
|
|
|
|
mCurrentAnimation.setPlayFraction(fraction);
|
2018-04-19 11:39:34 -07:00
|
|
|
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();
|
|
|
|
|
}
|
2018-03-21 09:32:27 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-27 11:05:26 -07:00
|
|
|
@Override
|
|
|
|
|
public void onDragEnd(float velocity, boolean fling) {
|
2018-03-14 17:51:49 -07:00
|
|
|
final int logAction;
|
2017-10-27 11:05:26 -07:00
|
|
|
final LauncherState targetState;
|
|
|
|
|
final float progress = mCurrentAnimation.getProgressFraction();
|
|
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
boolean blockedFling = fling && mBlockFling;
|
|
|
|
|
if (blockedFling) {
|
|
|
|
|
fling = false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 11:05:26 -07:00
|
|
|
if (fling) {
|
2018-03-14 17:51:49 -07:00
|
|
|
logAction = Touch.FLING;
|
|
|
|
|
targetState =
|
|
|
|
|
Float.compare(Math.signum(velocity), Math.signum(mProgressMultiplier)) == 0
|
|
|
|
|
? mToState : mFromState;
|
|
|
|
|
// snap to top or bottom using the release velocity
|
|
|
|
|
} else {
|
|
|
|
|
logAction = Touch.SWIPE;
|
|
|
|
|
targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final float endProgress;
|
|
|
|
|
final float startProgress;
|
|
|
|
|
final long duration;
|
2018-04-19 11:39:34 -07:00
|
|
|
// Increase the duration if we prevented the fling, as we are going against a high velocity.
|
|
|
|
|
final long durationMultiplier = blockedFling && targetState == mFromState ? 6 : 1;
|
2018-03-14 17:51:49 -07:00
|
|
|
|
|
|
|
|
if (targetState == mToState) {
|
|
|
|
|
endProgress = 1;
|
|
|
|
|
if (progress >= 1) {
|
|
|
|
|
duration = 0;
|
|
|
|
|
startProgress = 1;
|
2017-10-27 11:05:26 -07:00
|
|
|
} else {
|
2018-03-14 17:51:49 -07:00
|
|
|
startProgress = Utilities.boundToRange(
|
2018-03-21 09:32:27 -07:00
|
|
|
progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
|
2018-03-14 17:51:49 -07:00
|
|
|
duration = SwipeDetector.calculateDuration(velocity,
|
2018-04-19 11:39:34 -07:00
|
|
|
endProgress - Math.max(progress, 0)) * durationMultiplier;
|
2017-10-27 11:05:26 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
2018-05-04 13:17:46 -07:00
|
|
|
// Let the state manager know that the animation didn't go to the target state,
|
|
|
|
|
// but don't cancel ourselves (we already clean up when the animation completes).
|
|
|
|
|
Runnable onCancel = mCurrentAnimation.getOnCancelRunnable();
|
2018-04-27 12:33:24 -05:00
|
|
|
mCurrentAnimation.setOnCancelRunnable(null);
|
|
|
|
|
mCurrentAnimation.dispatchOnCancel();
|
2018-05-04 13:17:46 -07:00
|
|
|
mCurrentAnimation.setOnCancelRunnable(onCancel);
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
endProgress = 0;
|
|
|
|
|
if (progress <= 0) {
|
|
|
|
|
duration = 0;
|
|
|
|
|
startProgress = 0;
|
2017-10-27 11:05:26 -07:00
|
|
|
} else {
|
2018-03-14 17:51:49 -07:00
|
|
|
startProgress = Utilities.boundToRange(
|
2018-03-21 09:32:27 -07:00
|
|
|
progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
|
2018-03-14 17:51:49 -07:00
|
|
|
duration = SwipeDetector.calculateDuration(velocity,
|
2018-04-19 11:39:34 -07:00
|
|
|
Math.min(progress, 1) - endProgress) * durationMultiplier;
|
2017-10-27 11:05:26 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
|
2017-10-27 11:05:26 -07:00
|
|
|
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
|
2018-03-14 17:51:49 -07:00
|
|
|
anim.setFloatValues(startProgress, endProgress);
|
2018-04-19 11:39:34 -07:00
|
|
|
maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
|
|
|
|
|
updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
|
|
|
|
|
targetState, velocity, fling);
|
2018-04-27 12:33:24 -05:00
|
|
|
mCurrentAnimation.dispatchOnStart();
|
2017-10-27 11:05:26 -07:00
|
|
|
anim.start();
|
|
|
|
|
}
|
2017-12-12 12:02:29 -08:00
|
|
|
|
2018-04-19 11:39:34 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-21 09:32:27 -07:00
|
|
|
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
|
|
|
|
|
LauncherState targetState, float velocity, boolean isFling) {
|
|
|
|
|
animator.setDuration(expectedDuration)
|
|
|
|
|
.setInterpolator(scrollInterpolatorForVelocity(velocity));
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
protected int getDirectionForLog() {
|
|
|
|
|
return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
|
|
|
|
|
clearState();
|
2018-04-02 15:22:06 -07:00
|
|
|
boolean shouldGoToTargetState = true;
|
|
|
|
|
if (mPendingAnimation != null) {
|
|
|
|
|
boolean reachedTarget = mToState == targetState;
|
2018-04-06 12:25:10 -07:00
|
|
|
mPendingAnimation.finish(reachedTarget, logAction);
|
2018-04-02 15:22:06 -07:00
|
|
|
mPendingAnimation = null;
|
|
|
|
|
shouldGoToTargetState = !reachedTarget;
|
|
|
|
|
}
|
|
|
|
|
if (shouldGoToTargetState) {
|
2018-04-06 12:25:10 -07:00
|
|
|
if (targetState != mFromState) {
|
2018-04-24 13:42:59 -07:00
|
|
|
logReachedState(logAction);
|
2018-04-06 12:25:10 -07:00
|
|
|
}
|
2018-04-02 15:22:06 -07:00
|
|
|
mLauncher.getStateManager().goToState(targetState, false /* animated */);
|
|
|
|
|
}
|
2018-03-14 17:51:49 -07:00
|
|
|
}
|
|
|
|
|
|
2018-04-24 13:42:59 -07:00
|
|
|
private void logReachedState(int logAction) {
|
|
|
|
|
// Transition complete. log the action
|
|
|
|
|
mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
|
|
|
|
|
getDirectionForLog(),
|
|
|
|
|
mStartContainerType,
|
|
|
|
|
mFromState.containerType,
|
|
|
|
|
mToState.containerType,
|
|
|
|
|
mLauncher.getWorkspace().getCurrentPage());
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:51:49 -07:00
|
|
|
protected void clearState() {
|
|
|
|
|
mCurrentAnimation = null;
|
|
|
|
|
mDetector.finishedScrolling();
|
2018-04-27 12:33:24 -05:00
|
|
|
mDetector.setDetectableScrollConditions(0, false);
|
2018-03-14 17:51:49 -07:00
|
|
|
}
|
2017-10-27 11:05:26 -07:00
|
|
|
}
|