Using edgeEffect for overscroll in all-apps and widget tray
Bug: 183966408
Test: Manual
Change-Id: Iea1b67504fc59fc7ce2ec657bf8ac7e3d8bfff1c
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index bf0a88f..60dd774 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -77,7 +77,7 @@
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 @@
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 @@
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) {
@@ -407,12 +394,6 @@
}
}
- @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 */);
}
@@ -639,11 +620,8 @@
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 d0ec9d7..9701389 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<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 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 @@
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 29c00b2..d13884a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -144,17 +144,12 @@
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(