Merge "Add springs when snapping between pages on the workspace." into ub-launcher3-master

This commit is contained in:
Jonathan Miranda 2019-01-28 19:31:23 +00:00 committed by Android (Google) Code Review
commit d52b53e8e5
3 changed files with 132 additions and 8 deletions

View File

@ -18,6 +18,7 @@ package com.android.launcher3;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import android.animation.LayoutTransition; import android.animation.LayoutTransition;
@ -25,6 +26,7 @@ import android.animation.TimeInterpolator;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
@ -121,6 +123,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected boolean mIsPageInTransition = false; protected boolean mIsPageInTransition = false;
protected float mSpringOverScrollX;
protected boolean mWasInOverscroll = false; protected boolean mWasInOverscroll = false;
protected int mUnboundedScrollX; protected int mUnboundedScrollX;
@ -349,6 +353,11 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0); boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX); boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
if (!isXBeforeFirstPage && !isXAfterLastPage) {
mSpringOverScrollX = 0;
}
if (isXBeforeFirstPage) { if (isXBeforeFirstPage) {
super.scrollTo(mIsRtl ? mMaxScrollX : 0, y); super.scrollTo(mIsRtl ? mMaxScrollX : 0, y);
if (mAllowOverScroll) { if (mAllowOverScroll) {
@ -988,12 +997,35 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
} }
} }
@Override
protected void dispatchDraw(Canvas canvas) {
if (mScroller.isSpringing() && mSpringOverScrollX != 0) {
int saveCount = canvas.save();
canvas.translate(-mSpringOverScrollX, 0);
super.dispatchDraw(canvas);
canvas.restoreToCount(saveCount);
} else {
super.dispatchDraw(canvas);
}
}
protected void dampedOverScroll(int amount) { protected void dampedOverScroll(int amount) {
if (amount == 0) return; mSpringOverScrollX = amount;
if (amount == 0) {
return;
}
int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth()); int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
mSpringOverScrollX = overScrollAmount;
if (mScroller.isSpringing()) {
invalidate();
return;
}
if (amount < 0) { if (amount < 0) {
super.scrollTo(overScrollAmount, getScrollY()); super.scrollTo(amount, getScrollY());
} else { } else {
super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY()); super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
} }
@ -1001,6 +1033,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
} }
protected void overScroll(int amount) { protected void overScroll(int amount) {
mSpringOverScrollX = amount;
if (mScroller.isSpringing()) {
invalidate();
return;
}
if (amount == 0) return; if (amount == 0) return;
if (mFreeScroll && !mScroller.isFinished()) { if (mFreeScroll && !mScroller.isFinished()) {
@ -1372,7 +1410,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
// interpolator at zero, ie. 5. We use 4 to make it a little slower. // interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
return snapToPage(whichPage, delta, duration); if (QUICKSTEP_SPRINGS.get()) {
return snapToPage(whichPage, delta, duration, false, null,
velocity * Math.signum(newX - getUnboundedScrollX()), true);
} else {
return snapToPage(whichPage, delta, duration);
}
} }
public boolean snapToPage(int whichPage) { public boolean snapToPage(int whichPage) {
@ -1397,15 +1440,15 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
int newX = getScrollForPage(whichPage); int newX = getScrollForPage(whichPage);
final int delta = newX - getUnboundedScrollX(); final int delta = newX - getUnboundedScrollX();
return snapToPage(whichPage, delta, duration, immediate, interpolator); return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false);
} }
protected boolean snapToPage(int whichPage, int delta, int duration) { protected boolean snapToPage(int whichPage, int delta, int duration) {
return snapToPage(whichPage, delta, duration, false, null); return snapToPage(whichPage, delta, duration, false, null, 0, false);
} }
protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate, protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
TimeInterpolator interpolator) { TimeInterpolator interpolator, float velocity, boolean spring) {
if (mFirstLayout) { if (mFirstLayout) {
setCurrentPage(whichPage); setCurrentPage(whichPage);
return false; return false;
@ -1441,7 +1484,11 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
mScroller.setInterpolator(mDefaultInterpolator); mScroller.setInterpolator(mDefaultInterpolator);
} }
mScroller.startScroll(getUnboundedScrollX(), delta, duration); if (spring && QUICKSTEP_SPRINGS.get()) {
mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity);
} else {
mScroller.startScroll(getUnboundedScrollX(), delta, duration);
}
updatePageIndicator(); updatePageIndicator();

View File

@ -996,7 +996,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
@Override @Override
protected void overScroll(int amount) { protected void overScroll(int amount) {
boolean shouldScrollOverlay = mLauncherOverlay != null && boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl)); ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 && boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&

View File

@ -26,6 +26,11 @@ import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
/** /**
* Based on {@link android.widget.OverScroller} supporting only 1-d scrolling and with more * Based on {@link android.widget.OverScroller} supporting only 1-d scrolling and with more
* customization options. * customization options.
@ -196,6 +201,9 @@ public class OverScroller {
switch (mMode) { switch (mMode) {
case SCROLL_MODE: case SCROLL_MODE:
if (isSpringing()) {
return true;
}
long time = AnimationUtils.currentAnimationTimeMillis(); long time = AnimationUtils.currentAnimationTimeMillis();
// Any scroller can be used for time, since they were started // Any scroller can be used for time, since they were started
// together in scroll mode. We use X here. // together in scroll mode. We use X here.
@ -253,6 +261,22 @@ public class OverScroller {
mScroller.startScroll(start, delta, duration); mScroller.startScroll(start, delta, duration);
} }
/**
* Start scrolling using a spring by providing a starting point and the distance to travel.
*
* @param start Starting scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param delta Distance to travel. Positive numbers will scroll the
* content to the left.
* @param duration Duration of the scroll in milliseconds.
* @param velocity The starting velocity for the spring in px per ms.
*/
public void startScrollSpring(int start, int delta, int duration, float velocity) {
mMode = SCROLL_MODE;
mScroller.mState = mScroller.SPRING;
mScroller.startScroll(start, delta, duration, velocity);
}
/** /**
* Call this when you want to 'spring back' into a valid coordinate range. * Call this when you want to 'spring back' into a valid coordinate range.
* *
@ -354,6 +378,10 @@ public class OverScroller {
return (int) (time - mScroller.mStartTime); return (int) (time - mScroller.mStartTime);
} }
public boolean isSpringing() {
return mScroller.mState == SplineOverScroller.SPRING && !isFinished();
}
static class SplineOverScroller { static class SplineOverScroller {
// Initial position // Initial position
private int mStart; private int mStart;
@ -397,6 +425,8 @@ public class OverScroller {
// Current state of the animation. // Current state of the animation.
private int mState = SPLINE; private int mState = SPLINE;
private SpringAnimation mSpring;
// Constant gravity value, used in the deceleration phase. // Constant gravity value, used in the deceleration phase.
private static final float GRAVITY = 2000.0f; private static final float GRAVITY = 2000.0f;
@ -417,6 +447,20 @@ public class OverScroller {
private static final int SPLINE = 0; private static final int SPLINE = 0;
private static final int CUBIC = 1; private static final int CUBIC = 1;
private static final int BALLISTIC = 2; private static final int BALLISTIC = 2;
private static final int SPRING = 3;
private static final FloatPropertyCompat<SplineOverScroller> SPRING_PROPERTY =
new FloatPropertyCompat<SplineOverScroller>("splineOverScrollerSpring") {
@Override
public float getValue(SplineOverScroller scroller) {
return scroller.mCurrentPosition;
}
@Override
public void setValue(SplineOverScroller scroller, float value) {
scroller.mCurrentPosition = (int) value;
}
};
static { static {
float x_min = 0.0f; float x_min = 0.0f;
@ -465,6 +509,9 @@ public class OverScroller {
} }
void updateScroll(float q) { void updateScroll(float q) {
if (mState == SPRING) {
return;
}
mCurrentPosition = mStart + Math.round(q * (mFinal - mStart)); mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
} }
@ -495,6 +542,10 @@ public class OverScroller {
} }
void startScroll(int start, int distance, int duration) { void startScroll(int start, int distance, int duration) {
startScroll(start, distance, duration, 0);
}
void startScroll(int start, int distance, int duration, float velocity) {
mFinished = false; mFinished = false;
mCurrentPosition = mStart = start; mCurrentPosition = mStart = start;
@ -503,12 +554,31 @@ public class OverScroller {
mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = duration; mDuration = duration;
if (mState == SPRING) {
if (mSpring != null) {
mSpring.cancel();
}
mSpring = new SpringAnimation(this, SPRING_PROPERTY);
mSpring.setSpring(new SpringForce(mFinal)
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mSpring.setStartVelocity(velocity);
mSpring.animateToFinalPosition(mFinal);
mSpring.addEndListener((animation, canceled, value, velocity1) -> {
finish();
mState = SPLINE;
mSpring = null;
});
}
// Unused // Unused
mDeceleration = 0.0f; mDeceleration = 0.0f;
mVelocity = 0; mVelocity = 0;
} }
void finish() { void finish() {
if (mSpring != null && mSpring.isRunning()) mSpring.cancel();
mCurrentPosition = mFinal; mCurrentPosition = mFinal;
// Not reset since WebView relies on this value for fast fling. // Not reset since WebView relies on this value for fast fling.
// TODO: restore when WebView uses the fast fling implemented in this class. // TODO: restore when WebView uses the fast fling implemented in this class.
@ -518,6 +588,9 @@ public class OverScroller {
void setFinalPosition(int position) { void setFinalPosition(int position) {
mFinal = position; mFinal = position;
if (mState == SPRING && mSpring != null) {
mSpring.animateToFinalPosition(mFinal);
}
mSplineDistance = mFinal - mStart; mSplineDistance = mFinal - mStart;
mFinished = false; mFinished = false;
} }
@ -722,6 +795,10 @@ public class OverScroller {
* reached. * reached.
*/ */
boolean update() { boolean update() {
if (mState == SPRING) {
return mFinished;
}
final long time = AnimationUtils.currentAnimationTimeMillis(); final long time = AnimationUtils.currentAnimationTimeMillis();
final long currentTime = time - mStartTime; final long currentTime = time - mStartTime;