Merge "Add springs when snapping between pages on the workspace." into ub-launcher3-master
This commit is contained in:
commit
d52b53e8e5
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 &&
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue