diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 7d69950891..225e1c1440 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -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; } } diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java index d0ec9d7e08..9701389f91 100644 --- a/src/com/android/launcher3/views/SpringRelativeLayout.java +++ b/src/com/android/launcher3/views/SpringRelativeLayout.java @@ -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 DAMPED_SCROLL = - new FloatPropertyCompat("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(); } } } \ No newline at end of file diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 29c00b209f..d13884ad9e 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -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(