[Search][Motion] Introduce header protection in AllApps

ScrimView exposes ScrimDrawingController that allow FloatingHeaderView to observe AllApps scroll progress and draw header protection on Scrim. In addition, search box independently adopts header protection background to ensure proper clipping behavior.

Test: local
preview: https://drive.google.com/file/d/1_E577AAJ0LBg0zrKJQSEJK9GQnrtx7uv/view?usp=sharing&resourcekey=0-MTxjlB3xWyJ3qPvr4qMdig
Bug: 184946772
Change-Id: I64c0f4f50d26c475d31542148a15c7c145588d3f
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index fe0b11b..0adafaf 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -92,7 +92,8 @@
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
     <dimen name="all_apps_background_canvas_height">475dp</dimen>
     <dimen name="all_apps_header_pill_height">50dp</dimen>
-    <dimen name="all_apps_header_pill_corner_radius">50dp</dimen>
+    <dimen name="all_apps_header_pill_corner_radius">48dp</dimen>
+    <dimen name="all_apps_header_tab_height">48dp</dimen>
     <dimen name="all_apps_tabs_indicator_height">2dp</dimen>
     <dimen name="all_apps_header_top_padding">36dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index deb1147..5896f5a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1192,7 +1192,7 @@
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         mDropTargetBar.setup(mDragController);
-        mAllAppsController.setupViews(mAppsView);
+        mAllAppsController.setupViews(mScrimView, mAppsView);
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index b11b63e..47236b6 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -26,6 +26,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -44,6 +45,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
+import androidx.core.graphics.ColorUtils;
 import androidx.core.os.BuildCompat;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -66,6 +68,7 @@
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.ScrimView;
 import com.android.launcher3.views.SpringRelativeLayout;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
 
@@ -73,13 +76,16 @@
  * The all apps view container.
  */
 public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
-        Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
+        Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener,
+        ScrimView.ScrimDrawingController {
 
     private static final float FLING_VELOCITY_MULTIPLIER = 1000f;
 
     // Starts the springs after at least 25% of the animation has passed.
     private static final float FLING_ANIMATION_THRESHOLD = 0.25f;
 
+    private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
     protected final BaseDraggingActivity mLauncher;
     protected final AdapterHolder[] mAH;
     private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
@@ -93,7 +99,7 @@
     private View mSearchContainer;
     private AllAppsPagedView mViewPager;
 
-    private FloatingHeaderView mHeader;
+    protected FloatingHeaderView mHeader;
     private WorkModeSwitch mWorkModeSwitch;
 
 
@@ -107,7 +113,14 @@
 
     private Rect mInsets = new Rect();
 
-    SearchAdapterProvider mSearchAdapterProvider;
+    private SearchAdapterProvider mSearchAdapterProvider;
+    private final int mHeaderTopPadding;
+    private final int mScrimColor;
+    private final int mHeaderProtectionColor;
+    private final float mHeaderThreshold;
+    private ScrimView mScrimView;
+    private int mHeaderColor;
+
 
     public AllAppsContainerView(Context context) {
         this(context, null);
@@ -121,8 +134,19 @@
         super(context, attrs, defStyleAttr);
 
         mLauncher = BaseDraggingActivity.fromContext(context);
+
+        mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+        mHeaderThreshold = getResources().getDimensionPixelSize(
+                R.dimen.dynamic_grid_cell_border_spacing);
+        mHeaderTopPadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
+        int accentColor = Themes.getColorAccent(getContext());
+        mHeaderProtectionColor = ColorUtils.blendARGB(mScrimColor, accentColor, .3f);
+
         mLauncher.addOnDeviceProfileChangeListener(this);
 
+
+
         mSearchAdapterProvider = mLauncher.createSearchAdapterProvider(this);
         mSearchQueryBuilder = new SpannableStringBuilder();
         Selection.setSelection(mSearchQueryBuilder, 0);
@@ -300,6 +324,7 @@
         }
         // Reset the search bar and base recycler view after transitioning home
         mSearchUiManager.resetSearch();
+        updateHeaderScroll(0);
     }
 
     @Override
@@ -625,6 +650,26 @@
         outRect.offset(0, (int) getTranslationY());
     }
 
+    @Override
+    public void setTranslationY(float translationY) {
+        super.setTranslationY(translationY);
+        invalidateHeader();
+    }
+
+    public void setScrimView(ScrimView scrimView) {
+        mScrimView = scrimView;
+    }
+
+    @Override
+    public void drawOnScrim(Canvas canvas) {
+        mHeaderPaint.setColor(mHeaderColor);
+        mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+        if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
+            canvas.drawRect(0, 0, getWidth(), mHeaderTopPadding + getTranslationY(),
+                    mHeaderPaint);
+        }
+    }
+
     public class AdapterHolder {
         public static final int MAIN = 0;
         public static final int WORK = 1;
@@ -725,4 +770,24 @@
             return mOverlay;
         }
     }
+
+
+    protected void updateHeaderScroll(int scrolledOffset) {
+        float prog = Math.max(0, Math.min(1, (float) scrolledOffset / mHeaderThreshold));
+        int headerColor = ColorUtils.setAlphaComponent(mHeaderProtectionColor, (int) (prog * 255));
+        if (headerColor != mHeaderColor) {
+            mHeaderColor = headerColor;
+            getSearchView().setBackgroundColor(mHeaderColor);
+            invalidateHeader();
+        }
+    }
+
+    /**
+     * redraws header protection
+     */
+    public void invalidateHeader() {
+        if (mScrimView != null) {
+            mScrimView.invalidate();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index b1c764c..197d74c 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -38,6 +38,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.logging.StatsLogManager;
@@ -52,6 +53,7 @@
 public class AllAppsRecyclerView extends BaseRecyclerView {
     private static final String TAG = "AllAppsContainerView";
     private static final boolean DEBUG = false;
+    private final Launcher mLauncher;
 
     private AlphabeticalAppsList mApps;
     private final int mNumAppsPerRow;
@@ -87,6 +89,7 @@
                 R.dimen.all_apps_empty_search_bg_top_offset);
         mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
         mFastScrollHelper = new AllAppsFastScrollHelper(this);
+        mLauncher = Launcher.getLauncher(context);
     }
 
     /**
@@ -200,6 +203,12 @@
     }
 
     @Override
+    public void onScrolled(int dx, int dy) {
+        super.onScrolled(dx, dy);
+        mLauncher.getAppsView().updateHeaderScroll(getCurrentScrollY());
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent e) {
         boolean result = super.onInterceptTouchEvent(e);
         if (!result && e.getAction() == MotionEvent.ACTION_DOWN
@@ -410,7 +419,7 @@
 
     /**
      * Returns the available scroll height:
-     *   AvailableScrollHeight = Total height of the all items - last page height
+     * AvailableScrollHeight = Total height of the all items - last page height
      */
     @Override
     protected int getAvailableScrollHeight() {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c4c7891..90f2d7c 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.views.ScrimView;
 
 /**
  * Handles AllApps view transition.
@@ -88,6 +89,7 @@
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
 
     private float mScrollRangeDelta = 0;
+    private ScrimView mScrimView;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
@@ -158,7 +160,7 @@
 
         Interpolator interpolator = toState.equals(ALL_APPS)
                 ? (config.userControlled ? ACCEL_2 : ACCEL_0_75) :
-                        (config.userControlled ? DEACCEL_2 : DEACCEL);
+                (config.userControlled ? DEACCEL_2 : DEACCEL);
 
         Animator anim = createSpringAnimation(mProgress, targetProgress);
         anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
@@ -181,19 +183,28 @@
 
         Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
         setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
+
+        boolean shouldProtectHeader =
+                ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
+        mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
     }
 
     public AnimatorListenerAdapter getProgressAnimatorListener() {
         return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd);
     }
 
-    public void setupViews(AllAppsContainerView appsView) {
+    /**
+     * see Launcher#setupViews
+     */
+    public void setupViews(ScrimView scrimView, AllAppsContainerView appsView) {
+        mScrimView = scrimView;
         mAppsView = appsView;
         if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) {
             mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
                     View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         }
+        mAppsView.setScrimView(scrimView);
     }
 
     /**
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 5387a3e..0ba94ab 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
 
 import android.content.Context;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.util.AttributeSet;
@@ -37,13 +38,16 @@
 
     private SystemUiController mSystemUiController;
 
+    private ScrimDrawingController mDrawingController;
+
     public ScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setFocusable(false);
     }
 
     @Override
-    public void setInsets(Rect insets) { }
+    public void setInsets(Rect insets) {
+    }
 
     @Override
     public boolean hasOverlappingRendering() {
@@ -63,6 +67,14 @@
     }
 
     @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mDrawingController != null) {
+            mDrawingController.drawOnScrim(canvas);
+        }
+    }
+
+    @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
         updateSysUiColors();
@@ -96,4 +108,24 @@
         return ColorUtils.calculateLuminance(
                 ((ColorDrawable) getBackground()).getColor()) < 0.5f;
     }
+
+    /**
+     * Sets drawing controller. Invalidates ScrimView if drawerController has changed.
+     */
+    public void setDrawingController(ScrimDrawingController drawingController) {
+        if (mDrawingController != drawingController) {
+            mDrawingController = drawingController;
+            invalidate();
+        }
+    }
+
+    /**
+     * A Utility interface allowing for other surfaces to draw on ScrimView
+     */
+    public interface ScrimDrawingController {
+        /**
+         * Called inside ScrimView#OnDraw
+         */
+        void drawOnScrim(Canvas canvas);
+    }
 }