Loading src/com/android/launcher3/allapps/AllAppsContainerView.java +3 −25 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } /** Loading @@ -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) { Loading Loading @@ -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 */); } Loading Loading @@ -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; } } Loading src/com/android/launcher3/views/SpringRelativeLayout.java +75 −104 Original line number Diff line number Diff line Loading @@ -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; 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); } }; import com.android.launcher3.Utilities; protected final SparseBooleanArray mSpringViews = new SparseBooleanArray(); private final SpringAnimation mSpring; /** * View group to allow rendering overscroll effect in a child at the parent level */ public class SpringRelativeLayout extends RelativeLayout { private float mDampedScrollShift = 0; private SpringEdgeEffect mActiveEdge; private final EdgeEffect mEdgeGlowTop; private final EdgeEffect mEdgeGlowBottom; public SpringRelativeLayout(Context context) { this(context, null); Loading @@ -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; } return super.drawChild(canvas, child, drawingTime); 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(); } private void setActiveEdge(SpringEdgeEffect edge) { if (mActiveEdge != edge && mActiveEdge != null) { mActiveEdge.mDistance = 0; canvas.restoreToCount(restoreCount); } mActiveEdge = edge; 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(); } protected void setDampedScrollShift(float shift) { if (shift != mDampedScrollShift) { mDampedScrollShift = shift; invalidate(); 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 Loading @@ -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 src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +0 −5 Original line number Diff line number Diff line Loading @@ -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( Loading Loading
src/com/android/launcher3/allapps/AllAppsContainerView.java +3 −25 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } /** Loading @@ -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) { Loading Loading @@ -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 */); } Loading Loading @@ -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; } } Loading
src/com/android/launcher3/views/SpringRelativeLayout.java +75 −104 Original line number Diff line number Diff line Loading @@ -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; 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); } }; import com.android.launcher3.Utilities; protected final SparseBooleanArray mSpringViews = new SparseBooleanArray(); private final SpringAnimation mSpring; /** * View group to allow rendering overscroll effect in a child at the parent level */ public class SpringRelativeLayout extends RelativeLayout { private float mDampedScrollShift = 0; private SpringEdgeEffect mActiveEdge; private final EdgeEffect mEdgeGlowTop; private final EdgeEffect mEdgeGlowBottom; public SpringRelativeLayout(Context context) { this(context, null); Loading @@ -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; } return super.drawChild(canvas, child, drawingTime); 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(); } private void setActiveEdge(SpringEdgeEffect edge) { if (mActiveEdge != edge && mActiveEdge != null) { mActiveEdge.mDistance = 0; canvas.restoreToCount(restoreCount); } mActiveEdge = edge; 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(); } protected void setDampedScrollShift(float shift) { if (shift != mDampedScrollShift) { mDampedScrollShift = shift; invalidate(); 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 Loading @@ -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
src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +0 −5 Original line number Diff line number Diff line Loading @@ -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( Loading