[Predictive Back] Widget to home polish: show extra rows at bottom during animation

1. setClipChildren(false) for WidgetFullSheet and content view during animation
2. setClipToOutline(true) for WidgetsRecyclerView and provide ViewOutlineProvider to expand bottom by 5% of height
3. Override calculateExtraLayoutSpace() for ScrollableLayoutManager
4. Manually modify AbstractSlideInView#mContent's background drawable during scale animation

bug: b/260956481
Test: manual

Change-Id: Ic391639de887cf4a70bc4965dc0b1fd9bc12dd2c
This commit is contained in:
Fengjiang Li
2023-02-11 11:01:58 -08:00
parent 740541e0a0
commit cb640da2bb
14 changed files with 134 additions and 33 deletions

View File

@@ -17,15 +17,20 @@ package com.android.launcher3.views;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
@@ -33,10 +38,13 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -85,6 +93,10 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
protected @Nullable OnCloseListener mOnCloseBeginListener;
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
private final AnimatedFloat mSlidInViewScale = new AnimatedFloat(this::onScaleProgressChanged);
private boolean mIsBackProgressing;
@Nullable private Drawable mContentBackground;
public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mActivityContext = ActivityContext.lookupContext(context);
@@ -105,6 +117,10 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
}
protected void setContentBackground(Drawable drawable) {
mContentBackground = drawable;
}
protected void attachToContainer() {
if (mColorScrim != null) {
getPopupContainer().addView(mColorScrim);
@@ -132,6 +148,7 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
if (mColorScrim != null) {
mColorScrim.setAlpha(1 - mTranslationShift);
}
invalidate();
}
@Override
@@ -161,6 +178,68 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
return true;
}
@Override
public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) {
super.onBackProgressed(progress);
float deceleratedProgress =
Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress);
mIsBackProgressing = progress > 0f;
mSlidInViewScale.updateValue(PREDICTIVE_BACK_MIN_SCALE
+ (1 - PREDICTIVE_BACK_MIN_SCALE) * (1 - deceleratedProgress));
}
private void onScaleProgressChanged() {
float scaleProgress = mSlidInViewScale.value;
SCALE_PROPERTY.set(this, scaleProgress);
setClipChildren(!mIsBackProgressing);
mContent.setClipChildren(!mIsBackProgressing);
invalidate();
}
@Override
public void onBackInvoked() {
super.onBackInvoked();
animateSlideInViewToNoScale();
}
@Override
public void onBackCancelled() {
super.onBackCancelled();
animateSlideInViewToNoScale();
}
protected void animateSlideInViewToNoScale() {
mSlidInViewScale.animateToValue(1f)
.setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
.start();
}
@Override
protected void dispatchDraw(Canvas canvas) {
drawScaledBackground(canvas);
super.dispatchDraw(canvas);
}
/** Draw scaled background during predictive back animation. */
protected void drawScaledBackground(Canvas canvas) {
if (mContentBackground == null) {
return;
}
mContentBackground.setBounds(
mContent.getLeft(),
mContent.getTop() + (int) mContent.getTranslationY(),
mContent.getRight(),
mContent.getBottom() + (mIsBackProgressing ? getBottomOffsetPx() : 0));
mContentBackground.draw(canvas);
}
/** Return extra space revealed during predictive back animation. */
@Px
protected int getBottomOffsetPx() {
return (int) (getMeasuredHeight()
* (1 - PREDICTIVE_BACK_MIN_SCALE) / 2);
}
/**
* Returns {@code true} if the touch event is over the visible area of the bottom sheet.
*