Merge "Using edgeEffect for overscroll in all-apps and widget tray" into sc-dev
This commit is contained in:
@@ -77,7 +77,7 @@ import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePag
|
||||
public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
|
||||
Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
|
||||
|
||||
private static final float FLING_VELOCITY_MULTIPLIER = 135f;
|
||||
private static final float FLING_VELOCITY_MULTIPLIER = 1000 * .8f;
|
||||
// Starts the springs after at least 55% of the animation has passed.
|
||||
private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
|
||||
private static final int ALPHA_CHANNEL_COUNT = 2;
|
||||
@@ -140,12 +140,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
|
||||
mAllAppsStore.addUpdateListener(this::onAppsUpdated);
|
||||
|
||||
addSpringView(R.id.all_apps_header);
|
||||
addSpringView(R.id.apps_list_view);
|
||||
addSpringView(R.id.all_apps_tabs_view_pager);
|
||||
|
||||
mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,14 +164,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
return mWorkModeSwitch;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void setDampedScrollShift(float shift) {
|
||||
// Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
|
||||
float maxShift = getSearchView().getHeight() / 2f;
|
||||
super.setDampedScrollShift(Utilities.boundToRange(shift, -maxShift, maxShift));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceProfileChanged(DeviceProfile dp) {
|
||||
for (AdapterHolder holder : mAH) {
|
||||
@@ -408,12 +395,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCanvasClipTopForOverscroll() {
|
||||
// Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped.
|
||||
return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop();
|
||||
}
|
||||
|
||||
private void rebindAdapters(boolean showTabs) {
|
||||
rebindAdapters(showTabs, false /* force */);
|
||||
}
|
||||
@@ -640,11 +621,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
if (shouldSpring
|
||||
&& valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
|
||||
int searchViewId = getSearchView().getId();
|
||||
addSpringView(searchViewId);
|
||||
finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER,
|
||||
(anim, canceled, value, velocity) -> removeSpringView(searchViewId));
|
||||
|
||||
absorbSwipeUpVelocity(Math.abs(
|
||||
Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
|
||||
shouldSpring = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,51 +15,25 @@
|
||||
*/
|
||||
package com.android.launcher3.views;
|
||||
|
||||
import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
|
||||
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.View;
|
||||
import android.widget.EdgeEffect;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* View group to allow rendering overscroll effect in a child at the parent level
|
||||
*/
|
||||
public class SpringRelativeLayout extends RelativeLayout {
|
||||
|
||||
private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
|
||||
private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
private static final float VELOCITY_MULTIPLIER = 0.3f;
|
||||
|
||||
private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
|
||||
new FloatPropertyCompat<SpringRelativeLayout>("value") {
|
||||
|
||||
@Override
|
||||
public float getValue(SpringRelativeLayout object) {
|
||||
return object.mDampedScrollShift;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(SpringRelativeLayout object, float value) {
|
||||
object.setDampedScrollShift(value);
|
||||
}
|
||||
};
|
||||
|
||||
protected final SparseBooleanArray mSpringViews = new SparseBooleanArray();
|
||||
private final SpringAnimation mSpring;
|
||||
|
||||
private float mDampedScrollShift = 0;
|
||||
private SpringEdgeEffect mActiveEdge;
|
||||
private final EdgeEffect mEdgeGlowTop;
|
||||
private final EdgeEffect mEdgeGlowBottom;
|
||||
|
||||
public SpringRelativeLayout(Context context) {
|
||||
this(context, null);
|
||||
@@ -71,98 +45,73 @@ public class SpringRelativeLayout extends RelativeLayout {
|
||||
|
||||
public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
|
||||
mSpring.setSpring(new SpringForce(0)
|
||||
.setStiffness(STIFFNESS)
|
||||
.setDampingRatio(DAMPING_RATIO));
|
||||
}
|
||||
|
||||
public void addSpringView(int id) {
|
||||
mSpringViews.put(id, true);
|
||||
}
|
||||
|
||||
public void removeSpringView(int id) {
|
||||
mSpringViews.delete(id);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to clip the canvas when drawing child views during overscroll.
|
||||
*/
|
||||
public int getCanvasClipTopForOverscroll() {
|
||||
return 0;
|
||||
mEdgeGlowTop = Utilities.ATLEAST_S
|
||||
? new EdgeEffect(context, attrs) : new EdgeEffect(context);
|
||||
mEdgeGlowBottom = Utilities.ATLEAST_S
|
||||
? new EdgeEffect(context, attrs) : new EdgeEffect(context);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
|
||||
int saveCount = canvas.save();
|
||||
|
||||
canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight());
|
||||
canvas.translate(0, mDampedScrollShift);
|
||||
boolean result = super.drawChild(canvas, child, drawingTime);
|
||||
|
||||
canvas.restoreToCount(saveCount);
|
||||
|
||||
return result;
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
if (!mEdgeGlowTop.isFinished()) {
|
||||
final int restoreCount = canvas.save();
|
||||
canvas.translate(0, 0);
|
||||
mEdgeGlowTop.setSize(getWidth(), getHeight());
|
||||
if (mEdgeGlowTop.draw(canvas)) {
|
||||
postInvalidateOnAnimation();
|
||||
}
|
||||
canvas.restoreToCount(restoreCount);
|
||||
}
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
|
||||
private void setActiveEdge(SpringEdgeEffect edge) {
|
||||
if (mActiveEdge != edge && mActiveEdge != null) {
|
||||
mActiveEdge.mDistance = 0;
|
||||
}
|
||||
mActiveEdge = edge;
|
||||
}
|
||||
|
||||
protected void setDampedScrollShift(float shift) {
|
||||
if (shift != mDampedScrollShift) {
|
||||
mDampedScrollShift = shift;
|
||||
invalidate();
|
||||
if (!mEdgeGlowBottom.isFinished()) {
|
||||
final int restoreCount = canvas.save();
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
canvas.translate(-width, height);
|
||||
canvas.rotate(180, width, 0);
|
||||
mEdgeGlowBottom.setSize(width, height);
|
||||
if (mEdgeGlowBottom.draw(canvas)) {
|
||||
postInvalidateOnAnimation();
|
||||
}
|
||||
canvas.restoreToCount(restoreCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishScrollWithVelocity(float velocity) {
|
||||
mSpring.setStartVelocity(velocity);
|
||||
mSpring.setStartValue(mDampedScrollShift);
|
||||
mSpring.start();
|
||||
}
|
||||
|
||||
protected void finishWithShiftAndVelocity(float shift, float velocity,
|
||||
DynamicAnimation.OnAnimationEndListener listener) {
|
||||
setDampedScrollShift(shift);
|
||||
mSpring.addEndListener(listener);
|
||||
finishScrollWithVelocity(velocity);
|
||||
/**
|
||||
* Absorbs the velocity as a result for swipe-up fling
|
||||
*/
|
||||
protected void absorbSwipeUpVelocity(int velocity) {
|
||||
mEdgeGlowBottom.onAbsorb(velocity);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public EdgeEffectFactory createEdgeEffectFactory() {
|
||||
return new SpringEdgeEffectFactory();
|
||||
return new ProxyEdgeEffectFactory();
|
||||
}
|
||||
|
||||
private class SpringEdgeEffectFactory extends EdgeEffectFactory {
|
||||
private class ProxyEdgeEffectFactory extends EdgeEffectFactory {
|
||||
|
||||
@NonNull @Override
|
||||
protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
|
||||
switch (direction) {
|
||||
case DIRECTION_TOP:
|
||||
return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
|
||||
return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
|
||||
case DIRECTION_BOTTOM:
|
||||
return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
|
||||
return new EdgeEffectProxy(getContext(), mEdgeGlowBottom);
|
||||
}
|
||||
return super.createEdgeEffect(view, direction);
|
||||
}
|
||||
}
|
||||
|
||||
private class SpringEdgeEffect extends EdgeEffect {
|
||||
private class EdgeEffectProxy extends EdgeEffect {
|
||||
|
||||
private final float mVelocityMultiplier;
|
||||
private final EdgeEffect mParent;
|
||||
|
||||
private float mDistance;
|
||||
|
||||
public SpringEdgeEffect(Context context, float velocityMultiplier) {
|
||||
EdgeEffectProxy(Context context, EdgeEffect parent) {
|
||||
super(context);
|
||||
mVelocityMultiplier = velocityMultiplier;
|
||||
mParent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,22 +119,44 @@ public class SpringRelativeLayout extends RelativeLayout {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void invalidateParentScrollEffect() {
|
||||
if (!mParent.isFinished()) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbsorb(int velocity) {
|
||||
finishScrollWithVelocity(velocity * mVelocityMultiplier);
|
||||
mParent.onAbsorb(velocity);
|
||||
invalidateParentScrollEffect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPull(float deltaDistance) {
|
||||
mParent.onPull(deltaDistance);
|
||||
invalidateParentScrollEffect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPull(float deltaDistance, float displacement) {
|
||||
setActiveEdge(this);
|
||||
mDistance += deltaDistance * (mVelocityMultiplier / 3f);
|
||||
setDampedScrollShift(mDistance * getHeight());
|
||||
mParent.onPull(deltaDistance, displacement);
|
||||
invalidateParentScrollEffect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease() {
|
||||
mDistance = 0;
|
||||
finishScrollWithVelocity(0);
|
||||
mParent.onRelease();
|
||||
invalidateParentScrollEffect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
mParent.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return mParent.isFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,17 +144,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
findViewById(R.id.tab_work)
|
||||
.setOnClickListener((View view) -> mViewPager.snapToPage(1));
|
||||
fastScroller.setIsRecyclerViewFirstChildInParent(false);
|
||||
springLayout.addSpringView(R.id.primary_widgets_list_view);
|
||||
springLayout.addSpringView(R.id.work_widgets_list_view);
|
||||
} else {
|
||||
mViewPager = null;
|
||||
springLayout.addSpringView(R.id.primary_widgets_list_view);
|
||||
}
|
||||
|
||||
layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
|
||||
true);
|
||||
springLayout.addSpringView(R.id.search_and_recommendations_container);
|
||||
|
||||
mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
|
||||
findViewById(R.id.search_and_recommendations_container));
|
||||
mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
|
||||
|
||||
Reference in New Issue
Block a user