mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 19:38:21 +00:00
Make fling thresholds consistent
There are 3 places we can block a fling: - Swiping from home to all apps (through overview) - Swiping from an app to all apps (through overview) - Dismissing a task (in the same gesture that started by swiping down) In all of these cases, we block the fling when crossing the threshold to a new state (e.g. OVERVIEW), but unblock if the user pauses their drag. With this change, the logic is consistent: - Unblock the fling after pausing a short amount of time - If a fling was blocked, increase the settling duration based on velocity Bug: 78089840 Bug: 78658678 Change-Id: I5ef52b74229418b867b26c3c6d3db2cf6e48914b
This commit is contained in:
@@ -21,16 +21,17 @@ import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelo
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.LauncherAnimUtils;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.util.FlingBlockCheck;
|
||||
import com.android.launcher3.util.PendingAnimation;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
@@ -46,8 +47,6 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
|
||||
private static final String TAG = "OverviewSwipeController";
|
||||
|
||||
private static final float ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS = 0.1f;
|
||||
|
||||
// Progress after which the transition is assumed to be a success in case user does not fling
|
||||
private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
|
||||
|
||||
@@ -65,6 +64,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
private float mDisplacementShift;
|
||||
private float mProgressMultiplier;
|
||||
private float mEndDisplacement;
|
||||
private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
|
||||
|
||||
private TaskView mTaskBeingDragged;
|
||||
|
||||
@@ -93,7 +93,6 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
|
||||
Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
|
||||
clearState();
|
||||
}
|
||||
}
|
||||
@@ -158,16 +157,16 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
return mDetector.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
private boolean reInitAnimationController(boolean goingUp) {
|
||||
private void reInitAnimationController(boolean goingUp) {
|
||||
if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
|
||||
// No need to init
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
int scrollDirections = mDetector.getScrollDirections();
|
||||
if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
|
||||
|| !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
|
||||
// Trying to re-init in an unsupported direction.
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (mCurrentAnimation != null) {
|
||||
mCurrentAnimation.setPlayFraction(0);
|
||||
@@ -205,7 +204,6 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
mCurrentAnimation.getTarget().addListener(this);
|
||||
mCurrentAnimation.dispatchOnStart();
|
||||
mProgressMultiplier = 1 / mEndDisplacement;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -217,6 +215,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
|
||||
mCurrentAnimation.pause();
|
||||
}
|
||||
mFlingBlockCheck.unblockFling();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,6 +225,9 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
|
||||
if (isGoingUp != mCurrentAnimationIsGoingUp) {
|
||||
reInitAnimationController(isGoingUp);
|
||||
mFlingBlockCheck.blockFling();
|
||||
} else {
|
||||
mFlingBlockCheck.onEvent();
|
||||
}
|
||||
mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
|
||||
return true;
|
||||
@@ -235,22 +237,14 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
final boolean goingToEnd;
|
||||
final int logAction;
|
||||
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
|
||||
if (blockedFling) {
|
||||
fling = false;
|
||||
}
|
||||
if (fling) {
|
||||
logAction = Touch.FLING;
|
||||
boolean goingUp = velocity < 0;
|
||||
if (goingUp != mCurrentAnimationIsGoingUp) {
|
||||
// In case the fling is in opposite direction, make sure if is close enough
|
||||
// from the start position
|
||||
if (mCurrentAnimation.getProgressFraction()
|
||||
>= ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS) {
|
||||
// Not allowed
|
||||
goingToEnd = false;
|
||||
} else {
|
||||
goingToEnd = reInitAnimationController(goingUp);
|
||||
}
|
||||
} else {
|
||||
goingToEnd = true;
|
||||
}
|
||||
goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
|
||||
} else {
|
||||
logAction = Touch.SWIPE;
|
||||
goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
|
||||
@@ -259,6 +253,9 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
float progress = mCurrentAnimation.getProgressFraction();
|
||||
long animationDuration = SwipeDetector.calculateDuration(
|
||||
velocity, goingToEnd ? (1 - progress) : progress);
|
||||
if (blockedFling && !goingToEnd) {
|
||||
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
|
||||
}
|
||||
|
||||
float nextFrameProgress = Utilities.boundToRange(
|
||||
progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
@@ -26,6 +27,7 @@ import android.animation.ValueAnimator;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAnimUtils;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
@@ -33,7 +35,9 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
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;
|
||||
import com.android.launcher3.util.FlingBlockCheck;
|
||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.TransactionCompat;
|
||||
|
||||
@@ -44,7 +48,6 @@ import com.android.systemui.shared.system.TransactionCompat;
|
||||
*/
|
||||
public class LongSwipeHelper {
|
||||
|
||||
private static final float MIN_PROGRESS_TO_ALL_APPS = 0.35f;
|
||||
private static final float SWIPE_DURATION_MULTIPLIER =
|
||||
Math.min(1 / MIN_PROGRESS_TO_ALL_APPS, 1 / (1 - MIN_PROGRESS_TO_ALL_APPS));
|
||||
|
||||
@@ -53,6 +56,7 @@ public class LongSwipeHelper {
|
||||
|
||||
private float mMaxSwipeDistance = 1;
|
||||
private AnimatorPlaybackController mAnimator;
|
||||
private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
|
||||
|
||||
LongSwipeHelper(Launcher launcher, RemoteAnimationTargetSet targetSet) {
|
||||
mLauncher = launcher;
|
||||
@@ -62,6 +66,7 @@ public class LongSwipeHelper {
|
||||
|
||||
private void init() {
|
||||
setTargetAlpha(0, true);
|
||||
mFlingBlockCheck.blockFling();
|
||||
|
||||
// Init animations
|
||||
AllAppsTransitionController controller = mLauncher.getAllAppsController();
|
||||
@@ -74,6 +79,7 @@ public class LongSwipeHelper {
|
||||
|
||||
public void onMove(float displacement) {
|
||||
mAnimator.setPlayFraction(displacement / mMaxSwipeDistance);
|
||||
mFlingBlockCheck.onEvent();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
@@ -90,6 +96,11 @@ public class LongSwipeHelper {
|
||||
final boolean toAllApps;
|
||||
float endProgress;
|
||||
|
||||
boolean blockedFling = isFling && mFlingBlockCheck.isBlocked();
|
||||
if (blockedFling) {
|
||||
isFling = false;
|
||||
}
|
||||
|
||||
if (!isFling) {
|
||||
toAllApps = currentFraction > MIN_PROGRESS_TO_ALL_APPS;
|
||||
endProgress = toAllApps ? 1 : 0;
|
||||
@@ -114,7 +125,11 @@ public class LongSwipeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
mAnimator.setEndAction(() -> onSwipeAnimationComplete(toAllApps, isFling, callback));
|
||||
if (blockedFling && !toAllApps) {
|
||||
duration *= LauncherAnimUtils.blockedFlingDurationFactor(0);
|
||||
}
|
||||
final boolean finalIsFling = isFling;
|
||||
mAnimator.setEndAction(() -> onSwipeAnimationComplete(toAllApps, finalIsFling, callback));
|
||||
ValueAnimator animator = mAnimator.getAnimationPlayer();
|
||||
animator.setDuration(duration).setInterpolator(DEACCEL);
|
||||
animator.setFloatValues(currentFraction, endProgress);
|
||||
@@ -150,6 +165,7 @@ public class LongSwipeHelper {
|
||||
mLauncher.getStateManager().goToState(toAllApps ? ALL_APPS : OVERVIEW, false);
|
||||
if (!toAllApps) {
|
||||
DiscoveryBounce.showForOverviewIfNeeded(mLauncher);
|
||||
mLauncher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(true);
|
||||
}
|
||||
|
||||
mLauncher.getUserEventDispatcher().logStateChangeAction(
|
||||
|
||||
@@ -39,6 +39,9 @@ public class LauncherAnimUtils {
|
||||
public static final int SPRING_LOADED_TRANSITION_MS = 150;
|
||||
public static final int SPRING_LOADED_EXIT_DELAY = 500;
|
||||
|
||||
// The progress of an animation to all apps must be at least this far along to snap to all apps.
|
||||
public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f;
|
||||
|
||||
static WeakHashMap<Animator, Object> sAnimators = new WeakHashMap<Animator, Object>();
|
||||
static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() {
|
||||
public void onAnimationStart(Animator animation) {
|
||||
@@ -165,16 +168,8 @@ public class LauncherAnimUtils {
|
||||
}
|
||||
};
|
||||
|
||||
public static final Property<View, Float> ELEVATION =
|
||||
new Property<View, Float>(Float.class, "elevation") {
|
||||
@Override
|
||||
public Float get(View view) {
|
||||
return view.getElevation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(View view, Float elevation) {
|
||||
view.setElevation(elevation);
|
||||
}
|
||||
};
|
||||
/** Increase the duration if we prevented the fling, as we are going against a high velocity. */
|
||||
public static int blockedFlingDurationFactor(float velocity) {
|
||||
return (int) Utilities.boundToRange(Math.abs(velocity) / 2, 2f, 6f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.launcher3.touch;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
@@ -32,6 +33,7 @@ import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAnimUtils;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationComponents;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationConfig;
|
||||
@@ -43,6 +45,7 @@ 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;
|
||||
import com.android.launcher3.util.FlingBlockCheck;
|
||||
import com.android.launcher3.util.PendingAnimation;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
|
||||
@@ -80,7 +83,7 @@ public abstract class AbstractStateChangeTouchController
|
||||
private float mProgressMultiplier;
|
||||
private float mDisplacementShift;
|
||||
private boolean mCanBlockFling;
|
||||
private boolean mBlockFling;
|
||||
private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
|
||||
|
||||
private AnimatorSet mAtomicAnim;
|
||||
private boolean mPassedOverviewAtomicThreshold;
|
||||
@@ -241,7 +244,7 @@ public abstract class AbstractStateChangeTouchController
|
||||
mStartProgress = mCurrentAnimation.getProgressFraction();
|
||||
}
|
||||
mCanBlockFling = mFromState == NORMAL;
|
||||
mBlockFling = false;
|
||||
mFlingBlockCheck.unblockFling();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -253,16 +256,19 @@ public abstract class AbstractStateChangeTouchController
|
||||
if (progress <= 0) {
|
||||
if (reinitCurrentAnimation(false, isDragTowardPositive)) {
|
||||
mDisplacementShift = displacement;
|
||||
mBlockFling = mCanBlockFling;
|
||||
if (mCanBlockFling) {
|
||||
mFlingBlockCheck.blockFling();
|
||||
}
|
||||
}
|
||||
} else if (progress >= 1) {
|
||||
if (reinitCurrentAnimation(true, isDragTowardPositive)) {
|
||||
mDisplacementShift = displacement;
|
||||
mBlockFling = mCanBlockFling;
|
||||
if (mCanBlockFling) {
|
||||
mFlingBlockCheck.blockFling();
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
} else {
|
||||
mFlingBlockCheck.onEvent();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -325,7 +331,7 @@ public abstract class AbstractStateChangeTouchController
|
||||
final LauncherState targetState;
|
||||
final float progress = mCurrentAnimation.getProgressFraction();
|
||||
|
||||
boolean blockedFling = fling && mBlockFling;
|
||||
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
|
||||
if (blockedFling) {
|
||||
fling = false;
|
||||
}
|
||||
@@ -338,14 +344,17 @@ public abstract class AbstractStateChangeTouchController
|
||||
// snap to top or bottom using the release velocity
|
||||
} else {
|
||||
logAction = Touch.SWIPE;
|
||||
targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
|
||||
float successProgress = mToState == ALL_APPS
|
||||
? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS;
|
||||
targetState = (progress > successProgress) ? mToState : mFromState;
|
||||
}
|
||||
|
||||
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;
|
||||
final int durationMultiplier = blockedFling && targetState == mFromState
|
||||
? LauncherAnimUtils.blockedFlingDurationFactor(velocity) : 1;
|
||||
|
||||
if (targetState == mToState) {
|
||||
endProgress = 1;
|
||||
|
||||
52
src/com/android/launcher3/util/FlingBlockCheck.java
Normal file
52
src/com/android/launcher3/util/FlingBlockCheck.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.util;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* Determines whether a fling should be blocked. Currently we block flings when crossing thresholds
|
||||
* to new states, and unblock after a short duration.
|
||||
*/
|
||||
public class FlingBlockCheck {
|
||||
// Allow flinging to a new state after waiting this many milliseconds.
|
||||
private static final long UNBLOCK_FLING_PAUSE_DURATION = 200;
|
||||
|
||||
private boolean mBlockFling;
|
||||
private long mBlockFlingTime;
|
||||
|
||||
public void blockFling() {
|
||||
mBlockFling = true;
|
||||
mBlockFlingTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
public void unblockFling() {
|
||||
mBlockFling = false;
|
||||
mBlockFlingTime = 0;
|
||||
}
|
||||
|
||||
public void onEvent() {
|
||||
// We prevent flinging after passing a state, but allow it if the user pauses briefly.
|
||||
if (SystemClock.uptimeMillis() - mBlockFlingTime >= UNBLOCK_FLING_PAUSE_DURATION) {
|
||||
mBlockFling = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return mBlockFling;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user