Merge "Clears the widget resize frame when a directional key is pressed." into ub-launcher3-burnaby-polish
diff --git a/res/drawable/all_apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml
index a09f88f..5a2c9e8 100644
--- a/res/drawable/all_apps_search_bg.xml
+++ b/res/drawable/all_apps_search_bg.xml
@@ -14,7 +14,25 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/quantum_panel"
-    android:insetTop="@dimen/container_bounds_minus_quantum_panel_padding_inset"
-    android:insetBottom="@dimen/container_bounds_minus_quantum_panel_padding_inset" />
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/quantum_panel_bg_color" />
+            <corners
+                android:topLeftRadius="2dp"
+                android:topRightRadius="2dp" />
+        </shape>
+    </item>
+    <item
+        android:top="@dimen/all_apps_search_bar_bg_overflow"
+        android:left="@dimen/all_apps_search_bar_bg_overflow"
+        android:right="@dimen/all_apps_search_bar_bg_overflow"
+        android:bottom="0dp">
+
+        <shape android:shape="rectangle">
+            <stroke
+                android:width="@dimen/all_apps_search_bar_divider_width"
+                android:color="#1E000000"/>
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/widget_internal_focus_bg.xml b/res/drawable/widget_internal_focus_bg.xml
new file mode 100644
index 0000000..4d4bea6
--- /dev/null
+++ b/res/drawable/widget_internal_focus_bg.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Used as the widget host view background when giving focus to a child via keyboard. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true">
+        <shape android:shape="rectangle">
+            <stroke android:color="#fff" android:width="2dp" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 6500ebc..1a951f1 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -25,6 +25,8 @@
 
     <com.android.launcher3.DragLayer
         android:id="@+id/drag_layer"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index d0772ee..8bf9d64 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -26,6 +26,8 @@
 
     <com.android.launcher3.DragLayer
         android:id="@+id/drag_layer"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 802922e..2fc62c5 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -25,6 +25,8 @@
 
     <com.android.launcher3.DragLayer
         android:id="@+id/drag_layer"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 1bf54ee..a677fff 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -18,40 +18,74 @@
      will bake the left/right padding into that view's background itself. -->
 <com.android.launcher3.allapps.AllAppsContainerView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    launcher:revealBackground="@drawable/quantum_panel_shape">
 
-    <!-- Both android:focusable and android:focusableInTouchMode are needed for
-         the view to get focus change events. -->
-    <FrameLayout
-        android:id="@+id/search_box_container"
+    <View
+        android:id="@+id/reveal_view"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:focusable="false"
+        android:elevation="2dp"
+        android:visibility="invisible" />
+
+
+    <com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/main_content"
+        android:saveEnabled="false"
+        android:visibility="gone"
+        android:layout_gravity="center"
         android:focusable="true"
         android:focusableInTouchMode="true"
-        android:visibility="gone" />
+        android:elevation="15dp" >
 
-    <FrameLayout
-        android:id="@+id/content"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1">
-        <FrameLayout
-            android:id="@+id/all_apps_reveal"
+        <!-- DO NOT CHANGE THE ID -->
+        <com.android.launcher3.allapps.AllAppsRecyclerView
+            android:id="@+id/apps_list_view"
+            android:theme="@style/Theme.Light.CustomOverscroll"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:focusable="false"
-            android:elevation="2dp"
-            android:visibility="invisible" />
-        <include
-            layout="@layout/all_apps_container"
-            android:id="@+id/all_apps_container"
+            android:layout_gravity="center_horizontal|top"
+            android:clipToPadding="false"
+            android:focusable="true"
+            android:layout_marginTop="@dimen/all_apps_search_bar_height"
+            android:descendantFocusability="afterDescendants" />
+
+        <LinearLayout
+            android:id="@+id/search_container"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:visibility="gone" />
-    </FrameLayout>
+            android:saveEnabled="false"
+            android:layout_height="@dimen/all_apps_search_bar_height"
+            android:layout_gravity="start|top"
+            android:orientation="horizontal"
+            android:background="@drawable/all_apps_search_bg" >
+
+            <com.android.launcher3.ExtendedEditText
+                android:id="@+id/search_box_input"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@android:color/transparent"
+                android:focusableInTouchMode="true"
+                android:gravity="fill_horizontal|center_vertical"
+                android:hint="@string/all_apps_search_bar_hint"
+                android:inputType="text|textNoSuggestions|textCapWords"
+                android:imeOptions="actionSearch|flagNoExtractUi"
+                android:maxLines="1"
+                android:scrollHorizontally="true"
+                android:layout_marginLeft="@dimen/container_fastscroll_thumb_max_width"
+                android:layout_marginRight="@dimen/container_fastscroll_thumb_max_width"
+                android:singleLine="true"
+                android:textColor="#4c4c4c"
+                android:textColorHint="#9c9c9c"
+                android:textSize="16sp" />
+        </LinearLayout>
+
+    </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
 </com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
new file mode 100644
index 0000000..75b5c48
--- /dev/null
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@drawable/quantum_panel"
+    android:elevation="5dp"
+    android:orientation="vertical" >
+
+    <FrameLayout
+        android:id="@+id/folder_content_wrapper"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
+
+        <com.android.launcher3.FocusIndicatorView
+            android:id="@+id/focus_indicator"
+            android:layout_width="20dp"
+            android:layout_height="20dp" />
+
+        <com.android.launcher3.FolderPagedView
+            android:id="@+id/folder_content"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingLeft="8dp"
+            android:paddingRight="8dp"
+            android:paddingTop="8dp"
+            launcher:pageIndicator="@+id/folder_page_indicator" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/folder_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:clipChildren="false"
+        android:orientation="horizontal"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp" >
+
+        <com.android.launcher3.ExtendedEditText
+            android:id="@+id/folder_name"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"
+            android:background="#00000000"
+            android:fontFamily="sans-serif-condensed"
+            android:textStyle="bold"
+            android:gravity="center_horizontal"
+            android:hint="@string/folder_hint_text"
+            android:imeOptions="flagNoExtractUi"
+            android:paddingBottom="12dp"
+            android:paddingTop="4dp"
+            android:singleLine="true"
+            android:textColor="#EE777777"
+            android:textColorHighlight="#ffCCCCCC"
+            android:textColorHint="#ff808080"
+            android:textSize="14sp" />
+
+        <include
+            android:id="@+id/folder_page_indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="12dp"
+            android:layout_gravity="center_vertical"
+            layout="@layout/page_indicator" />
+
+    </LinearLayout>
+
+</com.android.launcher3.Folder>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 755634f..e64f83a 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -18,34 +18,29 @@
      will bake the left/right padding into that view's background itself. -->
 <com.android.launcher3.widget.WidgetsContainerView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/widgets_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:descendantFocusability="afterDescendants">
+    android:descendantFocusability="afterDescendants"
+    launcher:revealBackground="@drawable/quantum_panel_shape_dark">
 
-    <FrameLayout
-        android:id="@+id/content"
+    <View
+        android:id="@+id/reveal_view"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <FrameLayout
-            android:id="@+id/widgets_reveal_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:focusable="false"
-            android:elevation="2dp"
-            android:visibility="invisible" />
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:focusable="false"
+        android:elevation="2dp"
+        android:visibility="invisible" />
 
-        <!-- DO NOT CHANGE THE ID -->
-        <com.android.launcher3.widget.WidgetsRecyclerView
-            android:id="@+id/widgets_list_view"
-            android:theme="@style/Theme.Dark.CustomOverscroll"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:elevation="15dp"
-            android:visibility="gone" />
-    </FrameLayout>
+    <com.android.launcher3.widget.WidgetsRecyclerView
+        android:id="@+id/main_content"
+        android:theme="@style/Theme.Dark.CustomOverscroll"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:elevation="15dp"
+        android:visibility="gone" />
 
 </com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 827332a..82f33d1 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -83,6 +83,13 @@
         <attr name="pageIndicator" format="reference" />
     </declare-styleable>
 
+    <!-- BaseContainerView specific attributes. These attributes are used to customize
+         AllApps view and WidgetsView in xml. -->
+    <declare-styleable name="BaseContainerView">
+        <!-- Drawable to use for the reveal animation -->
+        <attr name="revealBackground" format="reference" />
+    </declare-styleable>
+
     <!-- XML attributes used by default_workspace.xml -->
     <declare-styleable name="Favorite">
         <attr name="className" format="string" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1025aba..a303dab 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,13 +20,16 @@
 <!-- Dynamic Grid -->
     <dimen name="dynamic_grid_edge_margin">6dp</dimen>
     <dimen name="dynamic_grid_search_bar_height">48dp</dimen>
-    <!-- We want 32dp extra for the tall search bar. -->
-    <dimen name="dynamic_grid_search_bar_height_tall">80dp</dimen>
+    <!-- We want 46dp extra for the tall search bar. -->
+    <dimen name="dynamic_grid_search_bar_height_tall">94dp</dimen>
     <dimen name="qsb_internal_padding_top">8dp</dimen>
     <dimen name="qsb_internal_padding_bottom">8dp</dimen>
+    <dimen name="dynamic_grid_search_bar_extra_top_padding">0dp</dimen>
+    <!-- Reduce the space between the status bar and the search bar when the search bar is tall -->
+    <dimen name="dynamic_grid_search_bar_negative_top_padding_short">-4dp</dimen>
     <dimen name="dynamic_grid_search_bar_bottom_padding">4dp</dimen>
     <!-- Reduce the padding between the search bar and workspace when the search bar is tall -->
-    <dimen name="dynamic_grid_search_bar_bottom_padding_short">-6dp</dimen>
+    <dimen name="dynamic_grid_search_bar_bottom_negative_padding_short">-6dp</dimen>
     <dimen name="dynamic_grid_search_bar_bottom_padding_tablet">16dp</dimen>
     <dimen name="dynamic_grid_page_indicator_height">20dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
@@ -74,7 +77,7 @@
     <dimen name="all_apps_grid_view_start_margin">0dp</dimen>
     <dimen name="all_apps_grid_section_y_offset">8dp</dimen>
     <dimen name="all_apps_grid_section_text_size">24sp</dimen>
-    <dimen name="all_apps_search_bar_height">48dp</dimen>
+    <dimen name="all_apps_search_bar_height">60dp</dimen>
     <dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
     <dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
     <dimen name="all_apps_icon_width_gap">24dp</dimen>
@@ -87,7 +90,20 @@
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
     <dimen name="all_apps_background_canvas_height">475dp</dimen>
 
-<!-- Widget tray -->
+    <!-- Search bar in All Apps -->
+    <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>
+
+    <!-- The overflow is used to create a bottom border, by drawing other three sides
+        outside the bounds. Ensure that:
+            all_apps_search_bar_bg_overflow < (-3 * all_apps_search_bar_divider_width)
+        -6dp is picked at random, any smaller value would do.
+    -->
+    <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen>
+    <dimen name="all_apps_search_bar_divider_width">1dp</dimen>
+
+    <!-- Widget tray -->
     <dimen name="widget_container_inset">8dp</dimen>
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6daa452..2838a22 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -56,7 +56,7 @@
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
-    <string name="all_apps_search_bar_hint">Search Apps</string>
+    <string name="all_apps_search_bar_hint">Search Apps&#8230;</string>
     <!-- Loading apps text. [CHAR_LIMIT=50] -->
     <string name="all_apps_loading_message">Loading Apps&#8230;</string>
     <!-- No-search-results text. [CHAR_LIMIT=50] -->
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index c118240..8bb8c55 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -17,32 +17,38 @@
 package com.android.launcher3;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.widget.LinearLayout;
+import android.view.View;
+import android.widget.FrameLayout;
 
 /**
  * A base container view, which supports resizing.
  */
-public abstract class BaseContainerView extends LinearLayout implements Insettable {
+public abstract class BaseContainerView extends FrameLayout implements Insettable {
 
     private final static String TAG = "BaseContainerView";
 
     // The window insets
-    private Rect mInsets = new Rect();
+    private final Rect mInsets = new Rect();
     // The bounds of the search bar.  Only the left, top, right are used to inset the
     // search bar and the height is determined by the measurement of the layout
-    private Rect mFixedSearchBarBounds = new Rect();
-    // The computed bounds of the search bar
-    private Rect mSearchBarBounds = new Rect();
+    private final Rect mFixedSearchBarBounds = new Rect();
     // The computed bounds of the container
-    protected Rect mContentBounds = new Rect();
+    protected final Rect mContentBounds = new Rect();
     // The computed padding to apply to the container to achieve the container bounds
-    private Rect mContentPadding = new Rect();
+    private final Rect mContentPadding = new Rect();
     // The inset to apply to the edges and between the search bar and the container
-    private int mContainerBoundsInset;
-    private boolean mHasSearchBar;
+    private final int mContainerBoundsInset;
+
+    private final Drawable mRevealDrawable;
+
+    private View mRevealView;
+    private View mContent;
 
     public BaseContainerView(Context context) {
         this(context, null);
@@ -55,6 +61,19 @@
     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.BaseContainerView, defStyleAttr, 0);
+        mRevealDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
+        a.recycle();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mContent = findViewById(R.id.main_content);
+        mRevealView = findViewById(R.id.reveal_view);
     }
 
     @Override
@@ -63,10 +82,6 @@
         updateBackgroundAndPaddings();
     }
 
-    protected void setHasSearchBar() {
-        mHasSearchBar = true;
-    }
-
     /**
      * Sets the search bar bounds for this container view to match.
      */
@@ -92,47 +107,48 @@
      */
     protected void updateBackgroundAndPaddings() {
         Rect padding;
-        Rect searchBarBounds = new Rect();
-        if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
-            // Use the default bounds
-            padding = new Rect(mInsets.left + mContainerBoundsInset,
-                    (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
-                    mInsets.right + mContainerBoundsInset,
-                    mInsets.bottom + mContainerBoundsInset);
-
-            // Special case -- we have the search bar, but no specific bounds, so just give it
-            // the inset bounds without a height.
-            searchBarBounds.set(mInsets.left + mContainerBoundsInset,
+        if (isValidSearchBarBounds(mFixedSearchBarBounds)) {
+            padding = new Rect(
+                    mFixedSearchBarBounds.left,
                     mInsets.top + mContainerBoundsInset,
-                    getMeasuredWidth() - (mInsets.right + mContainerBoundsInset), 0);
-        } else {
-            // Use the search bounds, if there is a search bar, the bounds will contain
-            // the offsets for the insets so we can ignore that
-            padding = new Rect(mFixedSearchBarBounds.left,
-                    (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
                     getMeasuredWidth() - mFixedSearchBarBounds.right,
-                    mInsets.bottom + mContainerBoundsInset);
-
-            // Use the search bounds
-            searchBarBounds.set(mFixedSearchBarBounds);
+                    mInsets.bottom + mContainerBoundsInset
+            );
+        } else {
+            padding = new Rect(
+                    mInsets.left + mContainerBoundsInset,
+                    mInsets.top + mContainerBoundsInset,
+                    mInsets.right + mContainerBoundsInset,
+                    mInsets.bottom + mContainerBoundsInset
+            );
         }
 
-        // If either the computed container padding has changed, or the computed search bar bounds
-        // has changed, then notify the container
-        if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) {
+        // The container padding changed, notify the container.
+        if (!padding.equals(mContentPadding)) {
             mContentPadding.set(padding);
             mContentBounds.set(padding.left, padding.top,
                     getMeasuredWidth() - padding.right,
                     getMeasuredHeight() - padding.bottom);
-            mSearchBarBounds.set(searchBarBounds);
-            onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
+            onUpdateBackgroundAndPaddings(padding);
         }
     }
 
-    /**
-     * To be implemented by container views to update themselves when the bounds changes.
-     */
-    protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding);
+    private void onUpdateBackgroundAndPaddings(Rect padding) {
+        // Apply the top-bottom padding to itself so that the launcher transition is
+        // clipped correctly
+        setPadding(0, padding.top, 0, padding.bottom);
+
+        InsetDrawable background = new InsetDrawable(mRevealDrawable,
+                padding.left, 0, padding.right, 0);
+        mRevealView.setBackground(background.getConstantState().newDrawable());
+        mContent.setBackground(background);
+
+        Rect bgPadding = new Rect();
+        background.getPadding(bgPadding);
+        onUpdateBgPadding(padding, bgPadding);
+    }
+
+    protected abstract void onUpdateBgPadding(Rect padding, Rect bgPadding);
 
     /**
      * Returns whether the search bar bounds we got are considered valid.
@@ -142,4 +158,12 @@
                 searchBarBounds.right <= getMeasuredWidth() &&
                 searchBarBounds.bottom <= getMeasuredHeight();
     }
+
+    public final View getContentView() {
+        return mContent;
+    }
+
+    public final View getRevealView() {
+        return mRevealView;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c6da584..61c4b1a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -103,6 +103,8 @@
     // QSB
     private int searchBarWidgetInternalPaddingTop, searchBarWidgetInternalPaddingBottom;
     private int searchBarTopPaddingPx;
+    private int tallSearchBarNegativeTopPaddingPx, normalSearchBarTopExtraPaddingPx;
+    private int searchBarTopExtraPaddingPx; // One of the above.
     private int normalSearchBarBottomPaddingPx, tallSearchBarBottomPaddingPx;
     private int searchBarBottomPaddingPx; // One of the above.
     private int normalSearchBarSpaceHeightPx, tallSearchBarSpaceHeightPx;
@@ -216,6 +218,10 @@
                 R.dimen.qsb_internal_padding_top);
         searchBarWidgetInternalPaddingBottom = res.getDimensionPixelSize(
                 R.dimen.qsb_internal_padding_bottom);
+        normalSearchBarTopExtraPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_search_bar_extra_top_padding);
+        tallSearchBarNegativeTopPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_search_bar_negative_top_padding_short);
         if (isTablet && !isVerticalBarLayout()) {
             searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
             normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
@@ -225,8 +231,9 @@
             searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
             normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
                     res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding);
-            tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
-                    res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding_short);
+            tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom
+                    + res.getDimensionPixelSize(
+                    R.dimen.dynamic_grid_search_bar_bottom_negative_padding_short);
         }
 
         // Calculate the actual text height
@@ -272,7 +279,7 @@
 
     /** Returns the amount of extra space to allocate to the search bar for vertical padding. */
     private int getSearchBarTotalVerticalPadding() {
-        return searchBarTopPaddingPx + searchBarBottomPaddingPx;
+        return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx;
     }
 
     /** Returns the width and height of the search bar, ignoring any padding. */
@@ -427,10 +434,13 @@
             hotseatBarHeightPx = shortHotseatBarHeightPx;
             searchBarSpaceHeightPx = tallSearchBarSpaceHeightPx;
             searchBarBottomPaddingPx = tallSearchBarBottomPaddingPx;
+            searchBarTopExtraPaddingPx = isPhone ? tallSearchBarNegativeTopPaddingPx
+                    : normalSearchBarTopExtraPaddingPx;
         } else {
             hotseatBarHeightPx = normalHotseatBarHeightPx;
             searchBarSpaceHeightPx = normalSearchBarSpaceHeightPx;
             searchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
+            searchBarTopExtraPaddingPx = normalSearchBarTopExtraPaddingPx;
         }
     }
 
@@ -445,6 +455,7 @@
         lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
         lp.width = searchBarBounds.width();
         lp.height = searchBarBounds.height();
+        lp.topMargin = searchBarTopExtraPaddingPx;
         if (hasVerticalBarLayout) {
             // Vertical search bar space -- The search bar is fixed in the layout to be on the left
             //                              of the screen regardless of RTL
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 9cc94ae..471c324 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -57,6 +57,7 @@
 import com.android.launcher3.UninstallDropTarget.UninstallSource;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.UiThreadCircularReveal;
 
@@ -421,7 +422,9 @@
      */
     @SuppressLint("InflateParams")
     static Folder fromXml(Launcher launcher) {
-        return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, null);
+        return (Folder) launcher.getLayoutInflater().inflate(
+                FeatureFlags.LAUNCHER3_ICON_NORMALIZATION
+                        ? R.layout.user_folder_icon_normalized : R.layout.user_folder, null);
     }
 
     /**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0599eb8..fc2b5f4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -97,6 +97,7 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -1408,7 +1409,7 @@
         if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
             mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
         } else {
-            mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
+            mAppsView.setSearchBarController(new DefaultAppSearchController());
         }
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
@@ -4098,7 +4099,8 @@
             AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
             if (info == null || appWidgetInfo == null ||
                     !info.provider.equals(appWidgetInfo.provider)) {
-                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
+                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId + " info=" + info
+                        + " appWidgetInfo=" + appWidgetInfo);
                 deleteWidgetInfo(item);
                 return;
             }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 18ae753..4185257 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -57,6 +57,8 @@
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mDragLayer = ((Launcher) context).getDragLayer();
         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+
+        setBackgroundResource(R.drawable.widget_internal_focus_bg);
     }
 
     @Override
@@ -241,6 +243,7 @@
     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         if (gainFocus) {
             mChildrenFocused = false;
+            dispatchChildFocus(false);
         }
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
     }
@@ -249,6 +252,9 @@
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
         dispatchChildFocus(focused != null);
+        if (focused != null) {
+            focused.setFocusableInTouchMode(false);
+        }
     }
 
     @Override
@@ -262,10 +268,9 @@
         return mChildrenFocused;
     }
 
-    private void dispatchChildFocus(boolean focused) {
-        if (getOnFocusChangeListener() != null) {
-            getOnFocusChangeListener().onFocusChange(this, focused || isFocused());
-        }
+    private void dispatchChildFocus(boolean childIsFocused) {
+        // The host view's background changes when selected, to indicate the focus is inside.
+        setSelected(childIsFocused);
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 6619aaf..2177f52 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -20,6 +20,7 @@
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupManager;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
@@ -32,13 +33,13 @@
 
     private static final String TAG = "LauncherBAHelper";
 
+    private static final String KEY_LAST_NOTIFIED_TIME = "backup_manager_last_notified";
+
     private static final String LAUNCHER_DATA_PREFIX = "L";
 
     static final boolean VERBOSE = false;
     static final boolean DEBUG = false;
 
-    private static BackupManager sBackupManager;
-
     /**
      * Notify the backup manager that out database is dirty.
      *
@@ -47,10 +48,29 @@
      * @param context application context
      */
     public static void dataChanged(Context context) {
-        if (sBackupManager == null) {
-            sBackupManager = new BackupManager(context);
+        dataChanged(context, 0);
+    }
+
+    /**
+     * Notify the backup manager that out database is dirty.
+     *
+     * <P>This does not force an immediate backup.
+     *
+     * @param context application context
+     * @param throttleMs duration in ms for which two consecutive calls to backup manager should
+     *                   not be made.
+     */
+    public static void dataChanged(Context context, long throttleMs) {
+        SharedPreferences prefs = Utilities.getPrefs(context);
+        long now = System.currentTimeMillis();
+        long lastTime = prefs.getLong(KEY_LAST_NOTIFIED_TIME, 0);
+
+        // User can manually change the system time, which could lead to now < lastTime.
+        // Re-backup in that case, as the backup will have a wrong lastModifiedTime.
+        if (now < lastTime || now >= (lastTime + throttleMs)) {
+            BackupManager.dataChanged(context.getPackageName());
+            prefs.edit().putLong(KEY_LAST_NOTIFIED_TIME, now).apply();
         }
-        sBackupManager.dataChanged();
     }
 
     private LauncherBackupHelper mHelper;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a34e001..774cca4 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -141,6 +141,8 @@
     AllAppsList mBgAllAppsList;
     // Entire list of widgets.
     WidgetsModel mBgWidgetsModel;
+    // Keep a clone of widgets that can be accessed from non-worker thread.
+    WidgetsModel mFgWidgetsModel;
 
     // The lock that must be acquired before referencing any static bg data structures.  Unlike
     // other locks, this one can generally be held long-term because we never expect any of these
@@ -243,6 +245,7 @@
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
+        mFgWidgetsModel = mBgWidgetsModel.clone();
         mIconCache = iconCache;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
@@ -2757,18 +2760,17 @@
             @SuppressWarnings("unchecked")
             final ArrayList<AppInfo> list
                     = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
-            final WidgetsModel widgetList = mBgWidgetsModel.clone();
             Runnable r = new Runnable() {
                 public void run() {
                     final long t = SystemClock.uptimeMillis();
                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
                         callbacks.bindAllApplications(list);
-                        callbacks.bindAllPackages(widgetList);
+                        callbacks.bindAllPackages(mFgWidgetsModel);
                     }
                     if (DEBUG_LOADERS) {
                         Log.d(TAG, "bound all " + list.size() + " apps from cache in "
-                                + (SystemClock.uptimeMillis()-t) + "ms");
+                                + (SystemClock.uptimeMillis() - t) + "ms");
                     }
                 }
             };
@@ -3403,20 +3405,18 @@
             @Override
             public void run() {
                 updateWidgetsModel(refresh);
-                final WidgetsModel model = mBgWidgetsModel.clone();
-
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         Callbacks cb = getCallback();
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindAllPackages(model);
+                            callbacks.bindAllPackages(mFgWidgetsModel);
                         }
                     }
                 });
                 // update the Widget entries inside DB on the worker thread.
                 LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
-                        model.getRawList());
+                        mFgWidgetsModel.getRawList());
             }
         });
     }
@@ -3427,6 +3427,7 @@
      * @see #loadAndBindWidgetsAndShortcuts
      */
     @Thunk void updateWidgetsModel(boolean refresh) {
+        Utilities.assertWorkerThread();
         PackageManager packageManager = mApp.getContext().getPackageManager();
         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
         widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
@@ -3454,6 +3455,7 @@
             }
         }
         mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
+        mFgWidgetsModel = mBgWidgetsModel.clone();
     }
 
     @Thunk static boolean isPackageDisabled(Context context, String packageName,
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 7674d2a..4760930 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -270,6 +270,9 @@
                 if (mListener != null) {
                     mListener.onSettingsChanged(arg, value);
                 }
+                if (extras.getBoolean(LauncherSettings.Settings.NOTIFY_BACKUP)) {
+                    LauncherBackupAgentHelper.dataChanged(getContext());
+                }
                 Bundle result = new Bundle();
                 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value);
                 return result;
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 8a5804f..01d670d 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -324,5 +324,8 @@
 
         public static final String EXTRA_VALUE = "value";
         public static final String EXTRA_DEFAULT_VALUE = "default_value";
+
+        // Extra for set_boolean method to also notify the backup manager of the change.
+        public static final String NOTIFY_BACKUP = "notify_backup";
     }
 }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 83b12a9..30cae31 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -80,13 +80,18 @@
  */
 public class LauncherStateTransitionAnimation {
 
+    private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
+
     /**
      * Private callbacks made during transition setup.
      */
-    static abstract class PrivateTransitionCallbacks {
-        float getMaterialRevealViewFinalAlpha(View revealView) {
-            return 0;
+    private static class PrivateTransitionCallbacks {
+        private final float materialRevealViewFinalAlpha;
+
+        PrivateTransitionCallbacks(float revealAlpha) {
+            materialRevealViewFinalAlpha = revealAlpha;
         }
+
         float getMaterialRevealViewStartFinalRadius() {
             return 0;
         }
@@ -97,7 +102,7 @@
         void onTransitionComplete() {}
     }
 
-    public static final String TAG = "LauncherStateTransitionAnimation";
+    public static final String TAG = "LSTAnimation";
 
     // Flags to determine how to set the layers on views before the transition animation
     public static final int BUILD_LAYER = 0;
@@ -121,11 +126,7 @@
             final boolean animated, final boolean startSearchAfterTransition) {
         final AllAppsContainerView toView = mLauncher.getAppsView();
         final View buttonView = mLauncher.getAllAppsButton();
-        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
-            @Override
-            public float getMaterialRevealViewFinalAlpha(View revealView) {
-                return 1f;
-            }
+        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
             @Override
             public float getMaterialRevealViewStartFinalRadius() {
                 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
@@ -152,8 +153,7 @@
         };
         // Only animate the search bar if animating from spring loaded mode back to all apps
         mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
-                Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
-                toView.getRevealView(), toView.getSearchBarView(), animated, cb);
+                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, cb);
     }
 
     /**
@@ -164,15 +164,9 @@
         final WidgetsContainerView toView = mLauncher.getWidgetsView();
         final View buttonView = mLauncher.getWidgetsButton();
 
-        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
-            @Override
-            public float getMaterialRevealViewFinalAlpha(View revealView) {
-                return 0.3f;
-            }
-        };
         mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
-                Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
-                toView.getRevealView(), null, animated, cb);
+                Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated,
+                new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS));
     }
 
     /**
@@ -200,16 +194,15 @@
      * Creates and starts a new animation to a particular overlay view.
      */
     @SuppressLint("NewApi")
-    private AnimatorSet startAnimationToOverlay(final Workspace.State fromWorkspaceState,
-            final Workspace.State toWorkspaceState, final View buttonView, final View toView,
-            final View contentView, final View revealView, final View overlaySearchBarView,
+    private AnimatorSet startAnimationToOverlay(
+            final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+            final View buttonView, final BaseContainerView toView,
             final boolean animated, final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.ATLEAST_LOLLIPOP;
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
-        final int itemsAlphaStagger =
-                res.getInteger(R.integer.config_overlayItemsAlphaStagger);
+        final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
 
         final View fromView = mLauncher.getWorkspace();
 
@@ -227,13 +220,17 @@
                 animated, layerViews);
 
         // Animate the search bar
-        startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
-                animated ? revealDuration : 0, overlaySearchBarView);
+        startWorkspaceSearchBarAnimation(
+                toWorkspaceState, animated ? revealDuration : 0, animation);
 
         Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
 
+        final View contentView = toView.getContentView();
+
         if (animated && initialized) {
             // Setup the reveal view animation
+            final View revealView = toView.getRevealView();
+
             int width = revealView.getMeasuredWidth();
             int height = revealView.getMeasuredHeight();
             float revealRadius = (float) Math.hypot(width / 2, height / 2);
@@ -247,9 +244,9 @@
             final float revealViewToXDrift;
             final float revealViewToYDrift;
             if (material) {
-                int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
-                        buttonView, null);
-                revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
+                int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(
+                        revealView, buttonView, null);
+                revealViewToAlpha = pCb.materialRevealViewFinalAlpha;
                 revealViewToYDrift = buttonViewToPanelDelta[1];
                 revealViewToXDrift = buttonViewToPanelDelta[0];
             } else {
@@ -274,15 +271,6 @@
             layerViews.put(revealView, BUILD_AND_SET_LAYER);
             animation.play(panelAlphaAndDrift);
 
-            if (overlaySearchBarView != null) {
-                overlaySearchBarView.setAlpha(0f);
-                ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
-                searchBarAlpha.setDuration(revealDuration / 2);
-                searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-                layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
-                animation.play(searchBarAlpha);
-            }
-
             // Setup the animation for the content view
             contentView.setVisibility(View.VISIBLE);
             contentView.setAlpha(0f);
@@ -430,12 +418,8 @@
             final Workspace.State toWorkspaceState, final int toWorkspacePage,
             final boolean animated, final Runnable onCompleteRunnable) {
         AllAppsContainerView appsView = mLauncher.getAppsView();
-        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
-            @Override
-            float getMaterialRevealViewFinalAlpha(View revealView) {
-                // No alpha anim from all apps
-                return 1f;
-            }
+        // No alpha anim from all apps
+        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
             @Override
             float getMaterialRevealViewStartFinalRadius() {
                 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
@@ -463,9 +447,8 @@
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
-                toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
-                appsView.getRevealView(), appsView.getSearchBarView(), animated,
-                onCompleteRunnable, cb);
+                toWorkspacePage, mLauncher.getAllAppsButton(), appsView,
+                animated, onCompleteRunnable, cb);
     }
 
     /**
@@ -475,11 +458,8 @@
             final Workspace.State toWorkspaceState, final int toWorkspacePage,
             final boolean animated, final Runnable onCompleteRunnable) {
         final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
-        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
-            @Override
-            float getMaterialRevealViewFinalAlpha(View revealView) {
-                return 0.3f;
-            }
+        PrivateTransitionCallbacks cb =
+                new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) {
             @Override
             public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
                     final View revealView, final View widgetsButtonView) {
@@ -491,19 +471,20 @@
                 };
             }
         };
-        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
-                toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
-                widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
-                onCompleteRunnable, cb);
+        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
+                fromWorkspaceState, toWorkspaceState,
+                toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
+                animated, onCompleteRunnable, cb);
     }
 
     /**
      * Creates and starts a new animation to the workspace.
      */
-    private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
-            final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView,
-            final View fromView, final View contentView, final View revealView,
-            final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable,
+    private AnimatorSet startAnimationToWorkspaceFromOverlay(
+            final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+            final int toWorkspacePage,
+            final View buttonView, final BaseContainerView fromView,
+            final boolean animated, final Runnable onCompleteRunnable,
             final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
@@ -528,8 +509,8 @@
                 toWorkspacePage, animated, layerViews);
 
         // Animate the search bar
-        startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
-                animated ? revealDuration : 0, overlaySearchBarView);
+        startWorkspaceSearchBarAnimation(
+                toWorkspaceState, animated ? revealDuration : 0, animation);
 
         Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
 
@@ -540,6 +521,8 @@
             }
 
             animation.play(updateTransitionStepAnim);
+            final View revealView = fromView.getRevealView();
+            final View contentView = fromView.getContentView();
 
             // hideAppsCustomizeHelper is called in some cases when it is already hidden
             // don't perform all these no-op animations. In particularly, this was causing
@@ -588,7 +571,7 @@
 
                 // Setup animation for the reveal panel alpha
                 final float revealViewToAlpha = !material ? 0f :
-                        pCb.getMaterialRevealViewFinalAlpha(revealView);
+                        pCb.materialRevealViewFinalAlpha;
                 if (revealViewToAlpha != 1f) {
                     ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
                             1f, revealViewToAlpha);
@@ -616,16 +599,6 @@
                 itemsAlpha.setInterpolator(decelerateInterpolator);
                 animation.play(itemsAlpha);
 
-                if (overlaySearchBarView != null) {
-                    overlaySearchBarView.setAlpha(1f);
-                    ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
-                    searchAlpha.setDuration(revealDuration / 2);
-                    searchAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-                    searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
-                    layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
-                    animation.play(searchAlpha);
-                }
-
                 if (material) {
                     // Animate the all apps button
                     float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
@@ -671,9 +644,6 @@
                         contentView.setTranslationY(0);
                         contentView.setAlpha(1);
                     }
-                    if (overlaySearchBarView != null) {
-                        overlaySearchBarView.setAlpha(1f);
-                    }
 
                     // This can hold unnecessary references to views.
                     cleanupAnimation();
@@ -729,36 +699,11 @@
     /**
      * Coordinates the workspace search bar animation along with the launcher state animation.
      */
-    private void startWorkspaceSearchBarAnimation(AnimatorSet animation,
-            final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, int duration,
-            View overlaySearchBar) {
+    private void startWorkspaceSearchBarAnimation(
+            final Workspace.State toWorkspaceState, int duration, AnimatorSet animation) {
         final SearchDropTargetBar.State toSearchBarState =
                 toWorkspaceState.getSearchDropTargetBarState();
-
-        if (overlaySearchBar != null) {
-            if (mLauncher.launcherCallbacksProvidesSearch()) {
-                if ((toWorkspaceState == Workspace.State.NORMAL) &&
-                        (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN)) {
-                    // If we are transitioning from the overlay to the workspace, then show the
-                    // workspace search bar immediately and let the overlay search bar fade out on
-                    // top
-                    mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
-                    return;
-                } else if (fromWorkspaceState == Workspace.State.NORMAL) {
-                    // If we are transitioning from the workspace to the overlay, then keep the
-                    // workspace search bar visible until the overlay search bar fades in on top
-                    animation.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
-                        }
-                    });
-                    return;
-                }
-            }
-        }
-        // Fallback to the default search bar animation otherwise
-        mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
+        mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration, animation);
     }
 
     /**
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 772a334..f7288a0 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -18,12 +18,18 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.util.Thunk;
@@ -34,35 +40,32 @@
  */
 public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
 
+    private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
+    private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
+    private static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
+
     /** The different states that the search bar space can be in. */
     public enum State {
-        INVISIBLE   (0f, 0f),
-        SEARCH_BAR  (1f, 0f),
-        DROP_TARGET (0f, 1f);
+        INVISIBLE             (0f, 0f, 0f),
+        INVISIBLE_TRANSLATED  (0f, 0f, -1f),
+        SEARCH_BAR            (1f, 0f, 0f),
+        DROP_TARGET           (0f, 1f, 0f);
 
         private final float mSearchBarAlpha;
         private final float mDropTargetBarAlpha;
+        private final float mTranslation;
 
-        State(float sbAlpha, float dtbAlpha) {
+        State(float sbAlpha, float dtbAlpha, float translation) {
             mSearchBarAlpha = sbAlpha;
             mDropTargetBarAlpha = dtbAlpha;
+            mTranslation = translation;
         }
 
-        float getSearchBarAlpha() {
-            return mSearchBarAlpha;
-        }
-
-        float getDropTargetBarAlpha() {
-            return mDropTargetBarAlpha;
-        }
     }
 
     private static int DEFAULT_DRAG_FADE_DURATION = 175;
 
-    private LauncherViewPropertyAnimator mDropTargetBarAnimator;
-    private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
-    private static final AccelerateInterpolator sAccelerateInterpolator =
-            new AccelerateInterpolator();
+    private AnimatorSet mCurrentAnimation;
 
     private State mState = State.SEARCH_BAR;
     @Thunk View mQSB;
@@ -116,49 +119,11 @@
 
         // Create the various fade animations
         mDropTargetBar.setAlpha(0f);
-        mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar);
-        mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator);
-        mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                // Ensure that the view is visible for the animation
-                mDropTargetBar.setVisibility(View.VISIBLE);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (mDropTargetBar != null) {
-                    AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
-                }
-            }
-        });
+        AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
     }
 
     public void setQsbSearchBar(View qsb) {
         mQSB = qsb;
-        if (mQSB != null) {
-            // Update the search ber animation
-            mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB);
-            mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator);
-            mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    // Ensure that the view is visible for the animation
-                    if (mQSB != null) {
-                        mQSB.setVisibility(View.VISIBLE);
-                    }
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mQSB != null) {
-                        AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
-                    }
-                }
-            });
-        } else {
-            mQSBSearchBarAnimator = null;
-        }
     }
 
     /**
@@ -166,6 +131,10 @@
      * the state is applied immediately.
      */
     public void animateToState(State newState, int duration) {
+        animateToState(newState, duration, null);
+    }
+
+    public void animateToState(State newState, int duration, AnimatorSet animation) {
         if (mState != newState) {
             mState = newState;
 
@@ -174,30 +143,63 @@
                     getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
             mAccessibilityEnabled = am.isEnabled();
 
-            animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
-                    duration);
-            animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
-                    duration);
+            if (mCurrentAnimation != null) {
+                mCurrentAnimation.cancel();
+                mCurrentAnimation = null;
+            }
+            mCurrentAnimation = null;
+
+            if (duration > 0) {
+                mCurrentAnimation = new AnimatorSet();
+                mCurrentAnimation.setDuration(duration);
+
+                animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
+            } else {
+                mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
+                AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+            }
+
+            if (mQSB != null) {
+                boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
+                        .isVerticalBarLayout();
+                float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
+
+                if (duration > 0) {
+                    int translationChange = Float.compare(mQSB.getTranslationY(), translation);
+
+                    animateAlpha(mQSB, mState.mSearchBarAlpha,
+                            translationChange == 0 ? DEFAULT_INTERPOLATOR
+                                    : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
+                                    : MOVE_UP_INTERPOLATOR));
+
+                    if (translationChange != 0) {
+                        mCurrentAnimation.play(
+                                ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
+                    }
+                } else {
+                    mQSB.setTranslationY(translation);
+                    mQSB.setAlpha(mState.mSearchBarAlpha);
+                    AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
+                }
+            }
+
+            // Start the final animation
+            if (duration > 0) {
+                if (animation != null) {
+                    animation.play(mCurrentAnimation);
+                } else {
+                    mCurrentAnimation.start();
+                }
+            }
         }
     }
 
-    /**
-     * Convenience method to animate the alpha of a view using hardware layers.
-     */
-    private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
-            int duration) {
-        if (v == null) {
-            return;
-        }
-
-        animator.cancel();
+    private void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
         if (Float.compare(v.getAlpha(), alpha) != 0) {
-            if (duration > 0) {
-                animator.alpha(alpha).withLayer().setDuration(duration).start();
-            } else {
-                v.setAlpha(alpha);
-                AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
-            }
+            ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
+            anim.setInterpolator(interpolator);
+            anim.addListener(new ViewVisiblilyUpdateHandler(v));
+            mCurrentAnimation.play(anim);
         }
     }
 
@@ -253,4 +255,24 @@
         mDeleteDropTarget.enableAccessibleDrag(enable);
         mUninstallDropTarget.enableAccessibleDrag(enable);
     }
+
+    private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter {
+        private final View mView;
+
+        ViewVisiblilyUpdateHandler(View v) {
+            mView = v;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            // Ensure that the view is visible for the animation
+            mView.setVisibility(View.VISIBLE);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation){
+            AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled);
+        }
+
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 56801ff..7b873d4 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -181,7 +181,7 @@
 
     enum State {
         NORMAL          (SearchDropTargetBar.State.SEARCH_BAR),
-        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE),
+        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED),
         SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET),
         OVERVIEW        (SearchDropTargetBar.State.INVISIBLE),
         OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index f9bb134..945125b 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -17,11 +17,9 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.InsetDrawable;
 import android.support.v7.widget.RecyclerView;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
@@ -32,8 +30,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseContainerView;
 import com.android.launcher3.BubbleTextView;
@@ -42,6 +39,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Folder;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
@@ -50,7 +48,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Thunk;
 
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
@@ -135,19 +132,19 @@
     private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
     private static final int MAX_NUM_MERGES_PHONE = 2;
 
-    @Thunk Launcher mLauncher;
-    @Thunk AlphabeticalAppsList mApps;
-    private AllAppsGridAdapter mAdapter;
-    private RecyclerView.LayoutManager mLayoutManager;
-    private RecyclerView.ItemDecoration mItemDecoration;
+    private final Launcher mLauncher;
+    private final AlphabeticalAppsList mApps;
+    private final AllAppsGridAdapter mAdapter;
+    private final RecyclerView.LayoutManager mLayoutManager;
+    private final RecyclerView.ItemDecoration mItemDecoration;
 
-    @Thunk View mContent;
-    @Thunk View mContainerView;
-    @Thunk View mRevealView;
-    @Thunk AllAppsRecyclerView mAppsRecyclerView;
-    @Thunk AllAppsSearchBarController mSearchBarController;
-    private ViewGroup mSearchBarContainerView;
-    private View mSearchBarView;
+    private AllAppsRecyclerView mAppsRecyclerView;
+    private AllAppsSearchBarController mSearchBarController;
+
+    private View mSearchContainer;
+    private ExtendedEditText mSearchInput;
+    private HeaderElevationController mElevationController;
+
     private SpannableStringBuilder mSearchQueryBuilder = null;
 
     private int mSectionNamesMargin;
@@ -159,14 +156,6 @@
     // This coordinate is relative to its parent
     private final Point mIconLastTouchPos = new Point();
 
-    private View.OnClickListener mSearchClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            Intent searchIntent = (Intent) v.getTag();
-            mLauncher.startActivitySafely(v, searchIntent, null);
-        }
-    };
-
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -236,14 +225,7 @@
             throw new RuntimeException("Expected search bar controller to only be set once");
         }
         mSearchBarController = searchController;
-        mSearchBarController.initialize(mApps, this);
-
-        // Add the new search view to the layout
-        View searchBarView = searchController.getView(mSearchBarContainerView);
-        mSearchBarContainerView.addView(searchBarView);
-        mSearchBarContainerView.setVisibility(View.VISIBLE);
-        mSearchBarView = searchBarView;
-        setHasSearchBar();
+        mSearchBarController.initialize(mApps, mSearchInput, mAppsRecyclerView, this);
 
         updateBackgroundAndPaddings();
     }
@@ -256,34 +238,6 @@
     }
 
     /**
-     * Returns the content view used for the launcher transitions.
-     */
-    public View getContentView() {
-        return mContainerView;
-    }
-
-    /**
-     * Returns the all apps search view.
-     */
-    public View getSearchBarView() {
-        return mSearchBarView;
-    }
-
-    /**
-     * Returns the reveal view used for the launcher transitions.
-     */
-    public View getRevealView() {
-        return mRevealView;
-    }
-
-    /**
-     * Returns an new instance of the default app search controller.
-     */
-    public AllAppsSearchBarController newDefaultAppSearchController() {
-        return new DefaultAppSearchController(getContext(), this, mAppsRecyclerView);
-    }
-
-    /**
      * Focuses the search field and begins an app search.
      */
     public void startAppsSearch() {
@@ -304,25 +258,24 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        boolean isRtl = Utilities.isRtl(getResources());
-        mAdapter.setRtl(isRtl);
-        mContent = findViewById(R.id.content);
+        mAdapter.setRtl(Utilities.isRtl(getResources()));
 
         // This is a focus listener that proxies focus from a view into the list view.  This is to
         // work around the search box from getting first focus and showing the cursor.
-        View.OnFocusChangeListener focusProxyListener = new View.OnFocusChangeListener() {
+        getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 if (hasFocus) {
                     mAppsRecyclerView.requestFocus();
                 }
             }
-        };
-        mSearchBarContainerView = (ViewGroup) findViewById(R.id.search_box_container);
-        mSearchBarContainerView.setOnFocusChangeListener(focusProxyListener);
-        mContainerView = findViewById(R.id.all_apps_container);
-        mContainerView.setOnFocusChangeListener(focusProxyListener);
-        mRevealView = findViewById(R.id.all_apps_reveal);
+        });
+
+        mSearchContainer = findViewById(R.id.search_container);
+        mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
+        mElevationController = Utilities.ATLEAST_LOLLIPOP
+                ? new HeaderElevationController.ControllerVL(mSearchContainer)
+                : new HeaderElevationController.ControllerV16(mSearchContainer);
 
         // Load the all apps recycler view
         mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
@@ -330,22 +283,28 @@
         mAppsRecyclerView.setLayoutManager(mLayoutManager);
         mAppsRecyclerView.setAdapter(mAdapter);
         mAppsRecyclerView.setHasFixedSize(true);
+        mAppsRecyclerView.addOnScrollListener(mElevationController);
+        mAppsRecyclerView.setElevationController(mElevationController);
+
         if (mItemDecoration != null) {
             mAppsRecyclerView.addItemDecoration(mItemDecoration);
         }
 
         // Precalculate the prediction icon and normal icon sizes
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST);
+        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST);
+
         BubbleTextView icon = (BubbleTextView) layoutInflater.inflate(
                 R.layout.all_apps_icon, this, false);
         icon.applyDummyInfo();
-        icon.measure(MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST));
+        icon.measure(widthMeasureSpec, heightMeasureSpec);
         BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate(
                 R.layout.all_apps_prediction_bar_icon, this, false);
         predIcon.applyDummyInfo();
-        predIcon.measure(MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST));
+        predIcon.measure(widthMeasureSpec, heightMeasureSpec);
         mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
                 icon.getMeasuredHeight());
 
@@ -360,8 +319,11 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Update the number of items in the grid before we measure the view
-        int availableWidth = !mContentBounds.isEmpty() ? mContentBounds.width() :
-                MeasureSpec.getSize(widthMeasureSpec);
+        // TODO: mSectionNamesMargin is currently 0, but also account for it,
+        // if it's enabled in the future.
+        int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
+                MeasureSpec.getSize(widthMeasureSpec))
+                    - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
         DeviceProfile grid = mLauncher.getDeviceProfile();
         grid.updateAppsViewNumCols(getResources(), availableWidth);
         if (mNumAppsPerRow != grid.allAppsNumCols ||
@@ -380,6 +342,12 @@
             mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
             mAdapter.setNumAppsPerRow(mNumAppsPerRow);
             mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
+
+            if (mNumAppsPerRow > 0) {
+                int iconSize = availableWidth / mNumAppsPerRow;
+                int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
+                mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
+            }
         }
 
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -391,51 +359,33 @@
      * recycler view to handle touch events (for fast scrolling) all the way to the edge.
      */
     @Override
-    protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
-        boolean isRtl = Utilities.isRtl(getResources());
-
-        // TODO: Use quantum_panel instead of quantum_panel_shape
-        InsetDrawable background = new InsetDrawable(
-                getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0,
-                padding.right, 0);
-        Rect bgPadding = new Rect();
-        background.getPadding(bgPadding);
-        mContainerView.setBackground(background);
-        mRevealView.setBackground(background.getConstantState().newDrawable());
+    protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
         mAppsRecyclerView.updateBackgroundPadding(bgPadding);
         mAdapter.updateBackgroundPadding(bgPadding);
+        mElevationController.updateBackgroundPadding(bgPadding);
 
         // Hack: We are going to let the recycler view take the full width, so reset the padding on
         // the container to zero after setting the background and apply the top-bottom padding to
         // the content view instead so that the launcher transition clips correctly.
-        mContent.setPadding(0, padding.top, 0, padding.bottom);
-        mContainerView.setPadding(0, 0, 0, 0);
+        getContentView().setPadding(0, 0, 0, 0);
 
         // Pad the recycler view by the background padding plus the start margin (for the section
         // names)
-        int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getMaxScrollbarWidth());
+        int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
+        int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
         int topBottomPadding = mRecyclerViewTopBottomPadding;
-        if (isRtl) {
-            mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getMaxScrollbarWidth(),
+        if (Utilities.isRtl(getResources())) {
+            mAppsRecyclerView.setPadding(padding.left + maxScrollBarWidth,
                     topBottomPadding, padding.right + startInset, topBottomPadding);
         } else {
             mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
-                    padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), topBottomPadding);
+                    padding.right + maxScrollBarWidth, topBottomPadding);
         }
 
-        // Inset the search bar to fit its bounds above the container
-        if (mSearchBarView != null) {
-            Rect backgroundPadding = new Rect();
-            if (mSearchBarView.getBackground() != null) {
-                mSearchBarView.getBackground().getPadding(backgroundPadding);
-            }
-            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                    mSearchBarContainerView.getLayoutParams();
-            lp.leftMargin = searchBarBounds.left - backgroundPadding.left;
-            lp.topMargin = searchBarBounds.top - backgroundPadding.top;
-            lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right;
-            mSearchBarContainerView.requestLayout();
-        }
+        MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
+        lp.leftMargin = padding.left;
+        lp.rightMargin = padding.right;
+        mSearchContainer.setLayoutParams(lp);
     }
 
     @Override
@@ -637,16 +587,18 @@
     @Override
     public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
         if (apps != null) {
-            mApps.setOrderedFilter(apps);
+            if (mApps.setOrderedFilter(apps)) {
+                mAppsRecyclerView.onSearchResultsChanged();
+            }
             mAdapter.setLastSearchQuery(query);
-            mAppsRecyclerView.onSearchResultsChanged();
         }
     }
 
     @Override
     public void clearSearchResult() {
-        mApps.setOrderedFilter(null);
-        mAppsRecyclerView.onSearchResultsChanged();
+        if (mApps.setOrderedFilter(null)) {
+            mAppsRecyclerView.onSearchResultsChanged();
+        }
 
         // Clear the search query
         mSearchQueryBuilder.clear();
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 1cb03c9..2b3d061 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -53,6 +53,8 @@
     private AllAppsBackgroundDrawable mEmptySearchBackground;
     private int mEmptySearchBackgroundTopOffset;
 
+    private HeaderElevationController mElevationController;
+
     public AllAppsRecyclerView(Context context) {
         this(context, null);
     }
@@ -83,6 +85,10 @@
         mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
     }
 
+    public void setElevationController(HeaderElevationController elevationController) {
+        mElevationController = elevationController;
+    }
+
     /**
      * Sets the number of apps per row in this recycler view.
      */
@@ -116,6 +122,9 @@
             mScrollbar.reattachThumbToScroll();
         }
         scrollToPosition(0);
+        if (mElevationController != null) {
+            mElevationController.reset();
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 2b363c0..fdce389 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -15,65 +15,179 @@
  */
 package com.android.launcher3.allapps;
 
-import android.content.ComponentName;
+import android.content.Context;
 import android.graphics.Rect;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
 
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.ComponentKey;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * An interface to a search box that AllApps can command.
  */
-public abstract class AllAppsSearchBarController {
+public abstract class AllAppsSearchBarController
+        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
 
+    private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
+
+    protected AllAppsRecyclerView mAppsRecyclerView;
     protected AlphabeticalAppsList mApps;
     protected Callbacks mCb;
+    protected ExtendedEditText mInput;
+
+    protected DefaultAppSearchAlgorithm mSearchAlgorithm;
+    protected InputMethodManager mInputMethodManager;
 
     /**
      * Sets the references to the apps model and the search result callback.
      */
-    public final void initialize(AlphabeticalAppsList apps, Callbacks cb) {
+    public final void initialize(
+            AlphabeticalAppsList apps, ExtendedEditText input,
+            AllAppsRecyclerView recycleView, Callbacks cb) {
         mApps = apps;
         mCb = cb;
-        onInitialize();
+        mAppsRecyclerView = recycleView;
+
+        mInput = input;
+        mInput.addTextChangedListener(this);
+        mInput.setOnEditorActionListener(this);
+        mInput.setOnBackKeyListener(this);
+
+        mInputMethodManager = (InputMethodManager)
+                mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        mSearchAlgorithm = onInitializeSearch();
     }
 
     /**
-     * To be overridden by subclasses.  This method will get called when the controller is set,
-     * before getView().
+     * To be overridden by subclasses. This method will get called when the controller is set.
      */
-    protected abstract void onInitialize();
+    protected DefaultAppSearchAlgorithm onInitializeSearch() {
+        return null;
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        // Do nothing
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        // Do nothing
+    }
+
+    @Override
+    public void afterTextChanged(final Editable s) {
+        String query = s.toString();
+        if (query.isEmpty()) {
+            mSearchAlgorithm.cancel(true);
+            mCb.clearSearchResult();
+        } else {
+            mSearchAlgorithm.cancel(false);
+            mSearchAlgorithm.doSearch(query, mCb);
+        }
+    }
+
+    @Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        // Skip if we disallow app-launch-on-enter
+        if (!ALLOW_SINGLE_APP_LAUNCH) {
+            return false;
+        }
+        // Skip if it's not the right action
+        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
+            return false;
+        }
+        // Skip if there are more than one icon
+        if (mApps.getNumFilteredApps() > 1) {
+            return false;
+        }
+        // Otherwise, find the first icon, or fallback to the search-market-view and launch it
+        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+        for (int i = 0; i < items.size(); i++) {
+            AlphabeticalAppsList.AdapterItem item = items.get(i);
+            switch (item.viewType) {
+                case AllAppsGridAdapter.ICON_VIEW_TYPE:
+                case AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE:
+                    mAppsRecyclerView.getChildAt(i).performClick();
+                    mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onBackKey() {
+        // Only hide the search field if there is no query, or if there
+        // are no filtered results
+        String query = Utilities.trim(mInput.getEditableText().toString());
+        if (query.isEmpty() || mApps.hasNoFilteredResults()) {
+            reset();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Resets the search bar state.
+     */
+    public void reset() {
+        unfocusSearchField();
+        mCb.clearSearchResult();
+        mInput.setText("");
+        mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
+    }
+
+    protected void unfocusSearchField() {
+        View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
+        if (nextFocus != null) {
+            nextFocus.requestFocus();
+        }
+    }
 
     /**
      * Returns the search bar view.
      * @param parent the parent to attach the search bar view to.
      */
-    public abstract View getView(ViewGroup parent);
+    public View getView(ViewGroup parent) {
+        return null;
+    }
 
     /**
      * Focuses the search field to handle key events.
      */
-    public abstract void focusSearchField();
+    public void focusSearchField() {
+        mInput.requestFocus();
+        mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
+    }
 
     /**
      * Returns whether the search field is focused.
      */
-    public abstract boolean isSearchFieldFocused();
-
-    /**
-     * Resets the search bar state.
-     */
-    public abstract void reset();
+    public boolean isSearchFieldFocused() {
+        return mInput.isFocused();
+    }
 
     /**
      * Returns whether the prediction bar should currently be visible depending on the state of
      * the search bar.
      */
-    @Deprecated
-    public abstract boolean shouldShowPredictionBar();
+    public boolean shouldShowPredictionBar() {
+        return false;
+    }
 
     /**
      * Callback for getting search results.
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index dac0df1..26e9231 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -278,11 +278,14 @@
     /**
      * Sets the sorted list of filtered components.
      */
-    public void setOrderedFilter(ArrayList<ComponentKey> f) {
+    public boolean setOrderedFilter(ArrayList<ComponentKey> f) {
         if (mSearchResults != f) {
+            boolean same = mSearchResults != null && mSearchResults.equals(f);
             mSearchResults = f;
             updateAdapterItems();
+            return !same;
         }
+        return false;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
index 3169f84..57747e3 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
@@ -15,261 +15,12 @@
  */
 package com.android.launcher3.allapps;
 
-import android.content.Context;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
-import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Thunk;
-
-import java.util.List;
-
-
 /**
  * The default search controller.
  */
-final class DefaultAppSearchController extends AllAppsSearchBarController
-        implements TextWatcher, TextView.OnEditorActionListener, View.OnClickListener {
+public class DefaultAppSearchController extends AllAppsSearchBarController {
 
-    private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
-
-    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;
-
-    private final Context mContext;
-    @Thunk final InputMethodManager mInputMethodManager;
-
-    private DefaultAppSearchAlgorithm mSearchManager;
-
-    private ViewGroup mContainerView;
-    private View mSearchView;
-    @Thunk View mSearchBarContainerView;
-    private View mSearchButtonView;
-    private View mDismissSearchButtonView;
-    @Thunk
-    ExtendedEditText mSearchBarEditView;
-    @Thunk AllAppsRecyclerView mAppsRecyclerView;
-    @Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mAppsRecyclerView.requestFocus();
-        }
-    };
-
-    public DefaultAppSearchController(Context context, ViewGroup containerView,
-            AllAppsRecyclerView appsRecyclerView) {
-        mContext = context;
-        mInputMethodManager = (InputMethodManager)
-                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
-        mContainerView = containerView;
-        mAppsRecyclerView = appsRecyclerView;
-    }
-
-    @Override
-    public View getView(ViewGroup parent) {
-        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-        mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false);
-        mSearchView.setOnClickListener(this);
-
-        mSearchButtonView = mSearchView.findViewById(R.id.search_button);
-        mSearchBarContainerView = mSearchView.findViewById(R.id.search_container);
-        mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
-        mDismissSearchButtonView.setOnClickListener(this);
-        mSearchBarEditView = (ExtendedEditText)
-                mSearchBarContainerView.findViewById(R.id.search_box_input);
-        mSearchBarEditView.addTextChangedListener(this);
-        mSearchBarEditView.setOnEditorActionListener(this);
-        mSearchBarEditView.setOnBackKeyListener(
-                new ExtendedEditText.OnBackKeyListener() {
-                    @Override
-                    public boolean onBackKey() {
-                        // Only hide the search field if there is no query, or if there
-                        // are no filtered results
-                        String query = Utilities.trim(
-                                mSearchBarEditView.getEditableText().toString());
-                        if (query.isEmpty() || mApps.hasNoFilteredResults()) {
-                            hideSearchField(true, mFocusRecyclerViewRunnable);
-                            return true;
-                        }
-                        return false;
-                    }
-                });
-        return mSearchView;
-    }
-
-    @Override
-    public void focusSearchField() {
-        mSearchBarEditView.requestFocus();
-        showSearchField();
-    }
-
-    @Override
-    public boolean isSearchFieldFocused() {
-        return mSearchBarEditView.isFocused();
-    }
-
-    @Override
-    protected void onInitialize() {
-        mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps());
-    }
-
-    @Override
-    public void reset() {
-        hideSearchField(false, null);
-    }
-
-    @Override
-    public boolean shouldShowPredictionBar() {
-        return false;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mSearchView) {
-            showSearchField();
-        } else if (v == mDismissSearchButtonView) {
-            hideSearchField(true, mFocusRecyclerViewRunnable);
-        }
-    }
-
-    @Override
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        // Do nothing
-    }
-
-    @Override
-    public void onTextChanged(CharSequence s, int start, int before, int count) {
-        // Do nothing
-    }
-
-    @Override
-    public void afterTextChanged(final Editable s) {
-        String query = s.toString();
-        if (query.isEmpty()) {
-            mSearchManager.cancel(true);
-            mCb.clearSearchResult();
-        } else {
-            mSearchManager.cancel(false);
-            mSearchManager.doSearch(query, mCb);
-        }
-    }
-
-    @Override
-    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-        // Skip if we disallow app-launch-on-enter
-        if (!ALLOW_SINGLE_APP_LAUNCH) {
-            return false;
-        }
-        // Skip if it's not the right action
-        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
-            return false;
-        }
-        // Skip if there are more than one icon
-        if (mApps.getNumFilteredApps() > 1) {
-            return false;
-        }
-        // Otherwise, find the first icon, or fallback to the search-market-view and launch it
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-        for (int i = 0; i < items.size(); i++) {
-            AlphabeticalAppsList.AdapterItem item = items.get(i);
-            switch (item.viewType) {
-                case AllAppsGridAdapter.ICON_VIEW_TYPE:
-                case AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE:
-                    mAppsRecyclerView.getChildAt(i).performClick();
-                    mInputMethodManager.hideSoftInputFromWindow(
-                            mContainerView.getWindowToken(), 0);
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Focuses the search field.
-     */
-    private void showSearchField() {
-        // Show the search bar and focus the search
-        final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
-                mContext.getResources().getDisplayMetrics());
-        mSearchBarContainerView.setVisibility(View.VISIBLE);
-        mSearchBarContainerView.setAlpha(0f);
-        mSearchBarContainerView.setTranslationX(translationX);
-        mSearchBarContainerView.animate()
-                .alpha(1f)
-                .translationX(0)
-                .setDuration(FADE_IN_DURATION)
-                .withLayer()
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        mSearchBarEditView.requestFocus();
-                        mInputMethodManager.showSoftInput(mSearchBarEditView,
-                                InputMethodManager.SHOW_IMPLICIT);
-                    }
-                });
-        mSearchButtonView.animate()
-                .alpha(0f)
-                .translationX(-translationX)
-                .setDuration(FADE_OUT_DURATION)
-                .withLayer();
-    }
-
-    /**
-     * Unfocuses the search field.
-     */
-    @Thunk void hideSearchField(boolean animated, final Runnable postAnimationRunnable) {
-        mSearchManager.cancel(true);
-
-        final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
-        final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
-                mContext.getResources().getDisplayMetrics());
-        if (animated) {
-            // Hide the search bar and focus the recycler view
-            mSearchBarContainerView.animate()
-                    .alpha(0f)
-                    .translationX(0)
-                    .setDuration(FADE_IN_DURATION)
-                    .withLayer()
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mSearchBarContainerView.setVisibility(View.INVISIBLE);
-                            if (resetTextField) {
-                                mSearchBarEditView.setText("");
-                            }
-                            mCb.clearSearchResult();
-                            if (postAnimationRunnable != null) {
-                                postAnimationRunnable.run();
-                            }
-                        }
-                    });
-            mSearchButtonView.setTranslationX(-translationX);
-            mSearchButtonView.animate()
-                    .alpha(1f)
-                    .translationX(0)
-                    .setDuration(FADE_OUT_DURATION)
-                    .withLayer();
-        } else {
-            mSearchBarContainerView.setVisibility(View.INVISIBLE);
-            if (resetTextField) {
-                mSearchBarEditView.setText("");
-            }
-            mCb.clearSearchResult();
-            mSearchButtonView.setAlpha(1f);
-            mSearchButtonView.setTranslationX(0f);
-            if (postAnimationRunnable != null) {
-                postAnimationRunnable.run();
-            }
-        }
-        mInputMethodManager.hideSoftInputFromWindow(mContainerView.getWindowToken(), 0);
+    public DefaultAppSearchAlgorithm onInitializeSearch() {
+        return new DefaultAppSearchAlgorithm(mApps.getApps());
     }
 }
diff --git a/src/com/android/launcher3/allapps/HeaderElevationController.java b/src/com/android/launcher3/allapps/HeaderElevationController.java
new file mode 100644
index 0000000..07f583c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/HeaderElevationController.java
@@ -0,0 +1,101 @@
+package com.android.launcher3.allapps;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.R;
+
+/**
+ * Helper class for controlling the header elevation in response to RecyclerView scroll.
+ */
+public abstract class HeaderElevationController extends RecyclerView.OnScrollListener {
+
+    private int mCurrentY = 0;
+
+    public void reset() {
+        mCurrentY = 0;
+        onScroll(mCurrentY);
+    }
+
+    @Override
+    public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+        mCurrentY += dy;
+        onScroll(mCurrentY);
+    }
+
+    public void updateBackgroundPadding(Rect bgPadding) { }
+
+    abstract void onScroll(int scrollY);
+
+    public static class ControllerV16 extends HeaderElevationController {
+
+        private final View mShadow;
+        private final float mScrollToElevation;
+
+        public ControllerV16(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[] {0x1E000000, 0x00000000}));
+            mShadow.setAlpha(0);
+
+            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+                    FrameLayout.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 updateBackgroundPadding(Rect bgPadding) {
+            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams();
+            lp.leftMargin = bgPadding.left;
+            lp.rightMargin = bgPadding.right;
+            mShadow.requestLayout();
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static class ControllerVL extends HeaderElevationController {
+
+        private final View mHeader;
+        private final float mMaxElevation;
+        private final float mScrollToElevation;
+
+        public ControllerVL(View header) {
+            mHeader = header;
+            mHeader.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+
+            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 = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
+            float newElevation = mMaxElevation * elevationPct;
+            if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
+                mHeader.setElevation(newElevation);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index b780f59..8b9ac1f 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -65,7 +65,6 @@
     private IconCache mIconCache;
 
     /* Recycler view related member variables */
-    private View mContent;
     private WidgetsRecyclerView mView;
     private WidgetsListAdapter mAdapter;
 
@@ -99,8 +98,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mContent = findViewById(R.id.content);
-        mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view);
+        mView = (WidgetsRecyclerView) getContentView();
         mView.setAdapter(mAdapter);
 
         // This extends the layout space so that preloading happen for the {@link RecyclerView}
@@ -120,15 +118,6 @@
     // Returns views used for launcher transitions.
     //
 
-    public View getContentView() {
-        return mView;
-    }
-
-    public View getRevealView() {
-        // TODO(hyunyoungs): temporarily use apps view transition.
-        return findViewById(R.id.widgets_reveal_view);
-    }
-
     public void scrollToTop() {
         mView.scrollToPosition(0);
     }
@@ -338,21 +327,8 @@
     //
     // Container rendering related.
     //
-
     @Override
-    protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
-        // Apply the top-bottom padding to the content itself so that the launcher transition is
-        // clipped correctly
-        mContent.setPadding(0, padding.top, 0, padding.bottom);
-
-        // TODO: Use quantum_panel_dark instead of quantum_panel_shape_dark.
-        InsetDrawable background = new InsetDrawable(
-                getResources().getDrawable(R.drawable.quantum_panel_shape_dark), padding.left, 0,
-                padding.right, 0);
-        Rect bgPadding = new Rect();
-        background.getPadding(bgPadding);
-        mView.setBackground(background);
-        getRevealView().setBackground(background.getConstantState().newDrawable());
+    protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
         mView.updateBackgroundPadding(bgPadding);
     }
 
diff --git a/tests/Android.mk b/tests/Android.mk
index eba4ade..d82f0b3 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -14,21 +14,17 @@
 #
 
 LOCAL_PATH := $(call my-dir)
-
-src_dirs := src
-res_dirs := res
-
 include $(CLEAR_VARS)
 
+src_dirs := src
 LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
 LOCAL_AAPT_FLAGS := --auto-add-overlay
 
-LOCAL_SDK_VERSION := 21
+LOCAL_SDK_VERSION := 23
 
 LOCAL_PACKAGE_NAME := Launcher3Tests
 
diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
index db3f72f..21df601 100644
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
@@ -108,15 +108,9 @@
         if (!android.os.Build.DEVICE.equals("hammerhead")) {
             return;
         }
-        assertEquals(mInvariantProfile.numRows, 4);
-        assertEquals(mInvariantProfile.numColumns, 4);
-        assertEquals((int) mInvariantProfile.numHotseatIcons, 5);
-
-        DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
-        DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
-
-        assertEquals(portraitProfile.allAppsNumCols, 3);
-        assertEquals(landscapeProfile.allAppsNumCols, 5); // not used
+        assertEquals(4, mInvariantProfile.numRows);
+        assertEquals(4, mInvariantProfile.numColumns);
+        assertEquals(5, mInvariantProfile.numHotseatIcons);
     }
 
     // Add more tests for other devices, however, running them once on a single device is enough
@@ -172,10 +166,11 @@
         Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
         int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
                 resources.getDisplayMetrics());
-        if (portraitProfile.isTablet) {
-            assertEquals(8 + 80 + 24, portraitHeight);
+        if (portraitProfile.isPhone) {
+            // This fails on some devices due to http://b/26884580 (portraitHeight is 101, not 100).
+            assertEquals(4 + 94 + 2, portraitHeight);
         } else {
-            assertEquals(8 + 80 + 2, portraitHeight);
+            assertEquals(8 + 94 + 24, portraitHeight);
         }
         // Make sure the height that we pass in the widget options bundle is the height of the
         // search bar + 8dps padding top and bottom.
@@ -183,7 +178,7 @@
         int portraitWidgetOptsHeight = portraitDimens.y;
         Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
         int landscapeWidgetOptsHeight = landscapeDimens.y;
-        assertEquals(8 + 80 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
+        assertEquals(8 + 94 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
                 resources.getDisplayMetrics()));
         if (!landscapeProfile.isVerticalBarLayout()) {
             assertEquals(portraitHeight, landscapeHeight);