Merge "[Predictive Back] Widget to home polish: show extra rows at bottom during animation" into tm-qpr-dev
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index a5f72ef..b76eef7 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -18,7 +18,6 @@
         android:id="@+id/widgets_bottom_sheet"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@drawable/bg_rounded_corner_bottom_sheet"
         android:paddingTop="@dimen/bottom_sheet_handle_margin"
         android:orientation="vertical">
         <View
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index e3f1fca..e31bf7a 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -25,7 +25,6 @@
         android:id="@+id/container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@drawable/bg_widgets_full_sheet"
         android:focusable="true"
         android:importantForAccessibility="no">
 
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
index 3dbe6f5..1c0037d 100644
--- a/res/layout/widgets_full_sheet_large_screen.xml
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -24,7 +24,6 @@
         android:id="@+id/container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@drawable/bg_widgets_full_sheet"
         android:focusable="true"
         android:importantForAccessibility="no">
 
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 2819b99..b02e3e3 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -47,6 +47,7 @@
         android:layout_height="wrap_content"
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="0dp"
+        android:clipToOutline="true"
         android:orientation="vertical">
 
         <TextView
diff --git a/res/layout/widgets_full_sheet_paged_view_large_screen.xml b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
index 6634345..edee352 100644
--- a/res/layout/widgets_full_sheet_paged_view_large_screen.xml
+++ b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
@@ -53,6 +53,7 @@
             android:id="@+id/search_and_recommendations_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:clipToOutline="true"
             android:orientation="vertical">
 
             <FrameLayout
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 2291943..366d2d2 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -31,6 +31,7 @@
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="16dp"
         android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+        android:clipToOutline="true"
         android:orientation="vertical">
 
         <TextView
diff --git a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
index 212cd55..c6a4f62 100644
--- a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
+++ b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
@@ -38,6 +38,7 @@
             android:id="@+id/search_and_recommendations_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:clipToOutline="true"
             android:orientation="vertical">
 
             <FrameLayout
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 7641728..14e3fa6 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -16,10 +16,10 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
-import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
+import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -526,7 +526,7 @@
                 public void getOutline(View view, Outline outline) {
                     @Px final int bottomOffsetPx =
                             (int) (ActivityAllAppsContainerView.this.getMeasuredHeight()
-                                    * SWIPE_ALL_APPS_TO_HOME_MIN_SCALE);
+                                    * PREDICTIVE_BACK_MIN_SCALE);
                     outline.setRect(
                             0,
                             0,
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 866932a..df383bf 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -21,7 +21,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
-import androidx.annotation.Px;
 import androidx.core.view.accessibility.AccessibilityEventCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityRecordCompat;
@@ -146,19 +145,6 @@
         }
 
         /**
-         * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra
-         * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap
-         * created during predictive back's scale animation from all apps to home.
-         */
-        @Override
-        protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
-            super.calculateExtraLayoutSpace(state, extraLayoutSpace);
-            @Px int extraSpacePx = (int) (getHeight()
-                    * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2);
-            extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
-        }
-
-        /**
          * Returns the number of rows before {@param adapterPosition}, including this position
          * which should not be counted towards the collection info.
          */
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index b618724..92c017c 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -61,6 +61,7 @@
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.ScrollableLayoutManager;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.ScrimView;
@@ -79,8 +80,7 @@
         implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
     // This constant should match the second derivative of the animator interpolator.
     public static final float INTERP_COEFF = 1.7f;
-    public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
-    private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
+    public static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
 
     private static final float NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.1f;
     private static final float SWIPE_DRAG_COMMIT_THRESHOLD =
@@ -280,8 +280,9 @@
 
         float deceleratedProgress =
                 Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress);
-        float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
-                + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
+        float scaleProgress = ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE
+                + (1 - ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE)
+                * (1 - deceleratedProgress);
 
         mAllAppScale.updateValue(scaleProgress);
     }
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
index 9bc4ddc..cb6ecaa 100644
--- a/src/com/android/launcher3/util/ScrollableLayoutManager.java
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java
@@ -20,6 +20,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Px;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
@@ -31,6 +32,10 @@
  */
 public class ScrollableLayoutManager extends GridLayoutManager {
 
+    public static final float PREDICTIVE_BACK_MIN_SCALE = 0.9f;
+    private static final float EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT =
+            (1 - PREDICTIVE_BACK_MIN_SCALE) / 2;
+
     // keyed on item type
     protected final SparseIntArray mCachedSizes = new SparseIntArray();
 
@@ -111,6 +116,13 @@
         return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
     }
 
+    @Override
+    protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
+        super.calculateExtraLayoutSpace(state, extraLayoutSpace);
+        @Px int extraSpacePx = (int) (getHeight() * EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT);
+        extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
+    }
+
     /**
      * Returns the sum of the height, in pixels, of this list adapter's items from index
      * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index f73347a..e2f1c04 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -17,15 +17,20 @@
 
 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.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 @@
     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 @@
         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 @@
         if (mColorScrim != null) {
             mColorScrim.setAlpha(1 - mTranslationShift);
         }
+        invalidate();
     }
 
     @Override
@@ -161,6 +178,68 @@
         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.
      *
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index bf521cc..4099302 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -113,6 +113,7 @@
         }
         mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
                 R.dimen.widget_cell_horizontal_padding);
+        setContentBackground(getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet));
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 2ce400e..66e3a43 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -17,9 +17,7 @@
 
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE;
 import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -31,6 +29,7 @@
 import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Outline;
 import android.graphics.Rect;
 import android.os.Process;
 import android.os.UserHandle;
@@ -42,6 +41,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
 import android.view.WindowInsets;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -62,7 +62,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.model.UserManagerState;
@@ -170,6 +169,18 @@
                 }
             };
 
+    private final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            outline.setRect(
+                    0,
+                    0,
+                    view.getMeasuredWidth(),
+                    view.getMeasuredHeight() + getBottomOffsetPx()
+            );
+        }
+    };
+
     private final int mTabsHeight;
     private final int mWidgetSheetContentHorizontalPadding;
 
@@ -195,6 +206,8 @@
     private int mOrientation;
     private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView;
 
+    private RecyclerViewFastScroller mFastScroller;
+
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         DeviceProfile dp = Launcher.getLauncher(context).getDeviceProfile();
@@ -213,6 +226,7 @@
 
         mUserManagerState.init(UserCache.INSTANCE.get(context),
                 context.getSystemService(UserManager.class));
+        setContentBackground(getContext().getDrawable(R.drawable.bg_widgets_full_sheet));
     }
 
     public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -224,6 +238,9 @@
         super.onFinishInflate();
         mContent = findViewById(R.id.container);
 
+        mContent.setOutlineProvider(mViewOutlineProvider);
+        mContent.setClipToOutline(true);
+
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
                 : R.layout.widgets_full_sheet_recyclerview;
@@ -233,14 +250,17 @@
         }
         layoutInflater.inflate(contentLayoutRes, mContent, true);
 
-        RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
+        mFastScroller = findViewById(R.id.fast_scroller);
         if (mIsTwoPane) {
-            fastScroller.setVisibility(GONE);
+            mFastScroller.setVisibility(GONE);
         }
         mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
         mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view));
         if (mHasWorkProfile) {
             mViewPager = findViewById(R.id.widgets_view_pager);
+            mViewPager.setOutlineProvider(mViewOutlineProvider);
+            mViewPager.setClipToOutline(true);
+            mViewPager.setClipChildren(false);
             mViewPager.initParentViews(this);
             mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
             mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
@@ -349,11 +369,8 @@
 
     @Override
     public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) {
-        float deceleratedProgress =
-                Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress);
-        float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
-                + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
-        SCALE_PROPERTY.set(this, scaleProgress);
+        super.onBackProgressed(progress);
+        mFastScroller.setVisibility(progress > 0 ? View.INVISIBLE : View.VISIBLE);
     }
 
     private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
@@ -859,6 +876,7 @@
     public void onBackInvoked() {
         if (mIsInSearchMode) {
             mSearchBar.reset();
+            animateSlideInViewToNoScale();
         } else {
             super.onBackInvoked();
         }
@@ -1003,6 +1021,9 @@
 
         void setup(WidgetsRecyclerView recyclerView) {
             mWidgetsRecyclerView = recyclerView;
+            mWidgetsRecyclerView.setOutlineProvider(mViewOutlineProvider);
+            mWidgetsRecyclerView.setClipToOutline(true);
+            mWidgetsRecyclerView.setClipChildren(false);
             mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
             mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
             mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);