Extending the shadow effect for search bar to lower devices

> Applying the background to the whole container instead of recycleview

Change-Id: Ifc90d05e0e96c41ba9aaf96b906211b101c2e197
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index 5e50191..03ba646 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!--
+     Copyright (C) 2015 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,44 +14,48 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/apps_list"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:elevation="15dp"
-    android:visibility="gone"
-    android:focusableInTouchMode="true">
+    android:focusableInTouchMode="true"
+    android:visibility="gone" >
+
     <com.android.launcher3.AppsContainerRecyclerView
         android:id="@+id/apps_list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/apps_search_bar_height"
         android:layout_gravity="center_horizontal|top"
+        android:layout_marginTop="@dimen/apps_search_bar_height"
         android:clipToPadding="false"
-        android:focusable="true"
-        android:descendantFocusability="afterDescendants" />
+        android:descendantFocusability="afterDescendants"
+        android:focusable="true" />
+
     <LinearLayout
         android:id="@+id/prediction_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/apps_search_bar_height"
         android:orientation="horizontal"
-        android:visibility="invisible">
+        android:visibility="invisible" >
     </LinearLayout>
 
     <!-- We always want the search bar on top, so it goes last. -->
+
     <FrameLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
         android:layout_height="@dimen/apps_search_bar_height"
-        android:background="@drawable/apps_search_bg">
+        android:background="@drawable/apps_search_bg" >
+
         <LinearLayout
             android:id="@+id/app_search_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:visibility="invisible">
+            android:visibility="invisible" >
+
             <ImageView
                 android:id="@+id/dismiss_search_button"
                 android:layout_width="wrap_content"
@@ -58,29 +63,31 @@
                 android:layout_gravity="start|center_vertical"
                 android:layout_marginLeft="4dp"
                 android:layout_marginStart="4dp"
-                android:paddingTop="13dp"
-                android:paddingBottom="13dp"
                 android:contentDescription="@string/all_apps_button_label"
+                android:paddingBottom="13dp"
+                android:paddingTop="13dp"
                 android:src="@drawable/ic_arrow_back_grey" />
+
             <com.android.launcher3.AppsContainerSearchEditTextView
                 android:id="@+id/app_search_box"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:paddingTop="16dp"
+                android:background="@android:color/transparent"
+                android:focusableInTouchMode="true"
+                android:gravity="fill_horizontal"
+                android:hint="@string/apps_view_search_bar_hint"
+                android:imeOptions="actionDone|flagNoExtractUi"
+                android:maxLines="1"
                 android:paddingBottom="16dp"
                 android:paddingLeft="8dp"
-                android:hint="@string/apps_view_search_bar_hint"
-                android:maxLines="1"
-                android:singleLine="true"
+                android:paddingTop="16dp"
                 android:scrollHorizontally="true"
-                android:gravity="fill_horizontal"
-                android:textSize="16sp"
+                android:singleLine="true"
                 android:textColor="#4c4c4c"
                 android:textColorHint="#9c9c9c"
-                android:imeOptions="actionDone|flagNoExtractUi"
-                android:focusableInTouchMode="true"
-                android:background="@android:color/transparent" />
+                android:textSize="16sp" />
         </LinearLayout>
+
         <ImageView
             android:id="@+id/search_button"
             android:layout_width="wrap_content"
@@ -88,9 +95,10 @@
             android:layout_gravity="end|center_vertical"
             android:layout_marginEnd="6dp"
             android:layout_marginRight="6dp"
-            android:paddingTop="13dp"
-            android:paddingBottom="13dp"
             android:contentDescription="@string/apps_view_search_bar_hint"
+            android:paddingBottom="13dp"
+            android:paddingTop="13dp"
             android:src="@drawable/ic_search_grey" />
     </FrameLayout>
+
 </FrameLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2fa16e7..b5acfbd 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -71,6 +71,10 @@
     <dimen name="drop_target_drag_padding">14dp</dimen>
     <dimen name="drop_target_text_size">14sp</dimen>
 
+    <dimen name="all_apps_header_max_elevation">4dp</dimen>
+    <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
+    <dimen name="all_apps_header_shadow_height">6dp</dimen>
+
 <!-- Dragging -->
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index 861767e..95da48f 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -146,9 +146,7 @@
         mNumPredictedAppsPerRow = numPredictedAppsPerRow;
     }
 
-    @Override
-    public void setBackground(Drawable background) {
-        super.setBackground(background);
+    public void updateBackgroundPadding(Drawable background) {
         background.getPadding(mBackgroundPadding);
     }
 
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index b752905..7c1aee2 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.support.v7.widget.RecyclerView;
@@ -58,9 +59,7 @@
     private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
     private static final boolean DYNAMIC_HEADER_ELEVATION = true;
     private static final boolean DISMISS_SEARCH_ON_BACK = true;
-    private static final float HEADER_ELEVATION_DP = 4;
-    // How far the user has to scroll in order to reach the full elevation
-    private static final float HEADER_SCROLL_TO_ELEVATION_DP = 16;
+
     private static final int FADE_IN_DURATION = 175;
     private static final int FADE_OUT_DURATION = 100;
     private static final int SEARCH_TRANSLATION_X_DP = 18;
@@ -83,6 +82,8 @@
     private View mDismissSearchButtonView;
     private AppsContainerSearchEditTextView mSearchBarEditView;
 
+    private HeaderElevationController mElevationController;
+
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
     // This coordinate is relative to this container view
@@ -174,6 +175,7 @@
      */
     public void hideHeaderBar() {
         mHeaderView.setVisibility(View.GONE);
+        mElevationController.disable();
         onUpdateBackgrounds();
         onUpdatePaddings();
     }
@@ -219,9 +221,13 @@
         // Fix the header view elevation if not dynamically calculating it
         mHeaderView = findViewById(R.id.header);
         mHeaderView.setOnClickListener(this);
-        if (Utilities.isLmpOrAbove() && !DYNAMIC_HEADER_ELEVATION) {
-            mHeaderView.setElevation(DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
-                getContext().getResources().getDisplayMetrics()));
+
+        mElevationController = Utilities.isLmpOrAbove() ?
+                new HeaderElevationControllerVL(mHeaderView) :
+                    new HeaderElevationControllerV16(mHeaderView);
+        if (!DYNAMIC_HEADER_ELEVATION) {
+            mElevationController.onScroll(getResources()
+                    .getDimensionPixelSize(R.dimen.all_apps_header_scroll_to_elevation));
         }
 
         // Fix the prediction bar size
@@ -335,6 +341,10 @@
         boolean hasSearchBar = (mSearchBarEditView != null) &&
                 (mSearchBarEditView.getVisibility() == View.VISIBLE);
 
+        // Set the background on the container, but let the recyclerView extend the full screen,
+        // so that the fast-scroller works on the edge as well.
+        mContentView.setPadding(0, 0, 0, 0);
+
         if (mFixedBounds.isEmpty()) {
             // If there are no fixed bounds, then use the default padding and insets
             setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
@@ -377,19 +387,16 @@
     @Override
     protected void onUpdateBackgrounds() {
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
-        boolean hasSearchBar = (mSearchBarEditView != null) &&
-                (mSearchBarEditView.getVisibility() == View.VISIBLE);
 
         // Update the background of the reveal view and list to be inset with the fixed bound
         // insets instead of the default insets
         // TODO: Use quantum_panel instead of quantum_panel_shape.
-        mAppsRecyclerView.setBackground(new InsetDrawable(
-                getContext().getResources().getDrawable(
-                        hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.quantum_panel_shape),
-                inset, 0, inset, 0));
-        getRevealView().setBackground(new InsetDrawable(
+        InsetDrawable background = new InsetDrawable(
                 getContext().getResources().getDrawable(R.drawable.quantum_panel_shape),
-                inset, 0, inset, 0));
+                inset, 0, inset, 0);
+        mContentView.setBackground(background);
+        mAppsRecyclerView.updateBackgroundPadding(background);
+        getRevealView().setBackground(background.getConstantState().newDrawable());
     }
 
     @Override
@@ -619,17 +626,8 @@
      */
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     private void onRecyclerViewScrolled() {
-        if (DYNAMIC_HEADER_ELEVATION && Utilities.isLmpOrAbove()) {
-            int elevation = DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
-                    getContext().getResources().getDisplayMetrics());
-            int scrollToElevation = DynamicGrid.pxFromDp(HEADER_SCROLL_TO_ELEVATION_DP,
-                    getContext().getResources().getDisplayMetrics());
-            float elevationPct = (float) Math.min(mRecyclerViewScrollY, scrollToElevation) /
-                    scrollToElevation;
-            float newElevation = elevation * elevationPct;
-            if (Float.compare(mHeaderView.getElevation(), newElevation) != 0) {
-                mHeaderView.setElevation(newElevation);
-            }
+        if (DYNAMIC_HEADER_ELEVATION) {
+            mElevationController.onScroll(mRecyclerViewScrollY);
         }
 
         mPredictionBarView.setTranslationY(-mRecyclerViewScrollY + mAppsRecyclerView.getPaddingTop());
@@ -839,4 +837,79 @@
     private InputMethodManager getInputMethodManager() {
         return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
     }
+
+    private static interface HeaderElevationController {
+
+        public void onScroll(int scrollY);
+
+        public void disable();
+    }
+
+    private static final class HeaderElevationControllerV16 implements HeaderElevationController {
+
+        private final View mShadow;
+
+        private final float mScrollToElevation;
+
+        public HeaderElevationControllerV16(View header) {
+            Resources res = header.getContext().getResources();
+            mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+
+            mShadow = new View(header.getContext());
+            mShadow.setBackground(new GradientDrawable(
+                    GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000}));
+            mShadow.setAlpha(0);
+
+            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+                    LayoutParams.MATCH_PARENT,
+                    res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height));
+            lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height;
+
+            ((ViewGroup) header.getParent()).addView(mShadow, lp);
+        }
+
+        @Override
+        public void onScroll(int scrollY) {
+            float elevationPct = (float) Math.min(scrollY, mScrollToElevation) /
+                    mScrollToElevation;
+            mShadow.setAlpha(elevationPct);
+        }
+
+        @Override
+        public void disable() {
+            ViewGroup parent = (ViewGroup) mShadow.getParent();
+            if (parent != null) {
+                parent.removeView(mShadow);
+            }
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private static final class HeaderElevationControllerVL implements HeaderElevationController {
+
+        private final View mHeader;
+        private final float mMaxElevation;
+        private final float mScrollToElevation;
+
+        public HeaderElevationControllerVL(View header) {
+            mHeader = header;
+
+            Resources res = header.getContext().getResources();
+            mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
+            mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+        }
+
+        @Override
+        public void onScroll(int scrollY) {
+            float elevationPct = (float) Math.min(scrollY, mScrollToElevation) /
+                    mScrollToElevation;
+            float newElevation = mMaxElevation * elevationPct;
+            if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
+                mHeader.setElevation(newElevation);
+            }
+        }
+
+        @Override
+        public void disable() { }
+    }
 }