Merge "Making some view properties exported. These will be visible in hierarchy viewer" into ub-launcher3-calgary
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1981b13..bce7120 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -76,7 +76,7 @@
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
-            android:theme="@style/Theme"
+            android:theme="@style/LauncherTheme"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="nosensor"
             android:configChanges="keyboard|keyboardHidden|navigation"
diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/WallpaperPicker/res/values-sw720dp/styles.xml
deleted file mode 100644
index 0058f7e..0000000
--- a/WallpaperPicker/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 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.
-*/
--->
-
-<resources>
-    <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionModeOverlay">true</item>
-    </style>
-</resources>
diff --git a/WallpaperPicker/res/values-v19/styles.xml b/WallpaperPicker/res/values-v19/styles.xml
index 15fb0ea..b465b4e7 100644
--- a/WallpaperPicker/res/values-v19/styles.xml
+++ b/WallpaperPicker/res/values-v19/styles.xml
@@ -24,9 +24,4 @@
         <item name="android:windowActionBarOverlay">true</item>
         <item name="android:windowTranslucentNavigation">true</item>
     </style>
-
-    <style name="Theme" parent="@style/BaseWallpaperTheme">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
-    </style>
 </resources>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
index de4b2f2..271fc2e 100644
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -32,12 +32,4 @@
         <item name="android:textColor">#ffffffff</item>
         <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
     </style>
-
-    <style name="Theme" parent="@style/BaseWallpaperTheme">
-        <item name="android:statusBarColor">#00000000</item>
-        <item name="android:navigationBarColor">#00000000</item>
-        <item name="android:colorControlActivated">@color/launcher_accent_color</item>
-        <item name="android:colorAccent">@color/launcher_accent_color</item>
-        <item name="android:colorPrimary">@color/launcher_accent_color</item>
-    </style>
 </resources>
\ No newline at end of file
diff --git a/WallpaperPicker/res/values/colors.xml b/WallpaperPicker/res/values/colors.xml
index 6ba32f0..adae7cf 100644
--- a/WallpaperPicker/res/values/colors.xml
+++ b/WallpaperPicker/res/values/colors.xml
@@ -19,6 +19,4 @@
 -->
 <resources>
     <color name="wallpaper_picker_translucent_gray">#66000000</color>
-
-    <color name="launcher_accent_color">#ff009688</color>
 </resources>
diff --git a/WallpaperPicker/res/values/styles.xml b/WallpaperPicker/res/values/styles.xml
index d1c945a..6c77d0a 100644
--- a/WallpaperPicker/res/values/styles.xml
+++ b/WallpaperPicker/res/values/styles.xml
@@ -35,15 +35,6 @@
         <item name="android:background">#88000000</item>
     </style>
 
-    <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowNoTitle">true</item>
-    </style>
-
-    <style name="Theme" parent="@style/BaseWallpaperTheme"></style>
-
     <style name="ActionBarSetWallpaperStyle" parent="@android:style/Widget.DeviceDefault.ActionButton">
         <item name="android:textColor">#ffffffff</item>
         <item name="android:background">?android:attr/selectableItemBackground</item>
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
new file mode 100644
index 0000000..a7b6429
--- /dev/null
+++ b/protos/launcher_log.proto
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.userevent.nano";
+option java_outer_classname = "LauncherLogProto";
+
+package userevent;
+
+message Target {
+  enum Type {
+    NONE = 0;
+    ITEM = 1;
+    CONTROL = 2;
+    CONTAINER = 3;
+  }
+
+  optional Type type = 1;
+
+  // For container type and item type
+  // Used mainly for ContainerType.FOLDER, ItemType.*
+  optional Target parent = 2;
+  optional int32 page_index = 3;
+  optional int32 rank = 4;
+  optional int32 grid_x = 5;
+  optional int32 grid_y = 6;
+
+  // For container types only
+  optional ContainerType container_type = 7;
+  optional int32 cardinality = 8;
+
+  // For control types only
+  optional ControlType control_type = 9;
+
+  // For item types only
+  optional ItemType item_type = 10;
+  optional int32 package_name_hash = 11;
+  optional int32 component_hash = 12;      // Used for ItemType.WIDGET
+  optional int32 intent_hash = 13;         // Used for ItemType.SHORTCUT
+  optional int32 span_x = 14 [default = 1];// Used for ItemType.WIDGET
+  optional int32 span_y = 15 [default = 1];// Used for ItemType.WIDGET
+}
+
+enum ItemType {
+  APP_ICON = 0;
+  SHORTCUT = 1;
+  WIDGET = 2;
+  FOLDER_ICON = 3;
+}
+
+enum ContainerType {
+  WORKSPACE = 0;
+  HOTSEAT = 1;
+  FOLDER = 2;
+  ALLAPPS = 3;
+  WIDGETS = 4;
+  OVERVIEW = 5;
+  PREDICTION = 6;
+  SEARCHRESULT = 7;
+}
+
+enum ControlType {
+  ALL_APPS_BUTTON = 0;
+  WIDGETS_BUTTON = 1;
+  WALLPAPER_BUTTON = 2;
+  SETTINGS_BUTTON = 3;
+  REMOVE_TARGET = 4;
+  UNINSTALL_TARGET = 5;
+  APPINFO_TARGET = 6;
+  RESIZE_HANDLE = 7;
+  FAST_SCROLL_HANDLE = 8;
+  // HOME, BACK, GO_TO_PLAYSTORE
+}
+
+message Action {
+  enum Type {
+    TOUCH = 0;
+    AUTOMATED = 1;
+    // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
+  }
+  enum Touch {
+    TAP = 0;
+    LONGPRESS = 1;
+    DRAGDROP = 2;
+    SWIPE = 3;
+    FLING = 4;
+    PINCH = 5;
+  }
+  optional Type type = 1;
+  optional Touch touch = 2;
+}
+
+//
+// Context free grammar of typical user interaction:
+//         Action (Touch) + Target
+//         Action (Touch) + Target + Target
+//
+message LauncherEvent {
+
+  required Action action = 1;
+
+  // List of targets that touch actions can be operated on.
+  optional Target src_target = 2;
+  optional Target dest_target = 3;
+
+  optional int64 action_duration_millis = 4;
+  optional int64 elapsed_container_millis = 5;
+  optional int64 elapsed_session_millis = 6;
+}
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/bg_celllayout.xml b/res/drawable/bg_celllayout.xml
new file mode 100644
index 0000000..0ce083e
--- /dev/null
+++ b/res/drawable/bg_celllayout.xml
@@ -0,0 +1,35 @@
+<?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.
+*/
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+            <solid android:color="#3fffffff" />
+        </shape>
+    </item>
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+            <solid android:color="#3fffffff" />
+            <stroke android:width="1dp" android:color="#fff" />
+        </shape>
+    </item>
+
+</transition>
\ No newline at end of file
diff --git a/res/drawable/bg_screenpanel.xml b/res/drawable/bg_screenpanel.xml
index cdb71df..346fca0 100644
--- a/res/drawable/bg_screenpanel.xml
+++ b/res/drawable/bg_screenpanel.xml
@@ -18,6 +18,7 @@
 */
 -->
 
+<!-- TODO(twickham): Remove this file and the screenpanel drawables -->
 <transition xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <item android:drawable="@drawable/screenpanel"/>
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 d166df7..aac0f83 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -25,6 +25,8 @@
 
     <com.android.launcher3.dragndrop.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 b618b53..fb015f8 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -26,6 +26,8 @@
 
     <com.android.launcher3.dragndrop.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 f976e30..a45858c 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -25,6 +25,8 @@
 
     <com.android.launcher3.dragndrop.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..c2b1e6f 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -18,40 +18,72 @@
      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: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: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/all_apps_container.xml b/res/layout/all_apps_container.xml
index 626edaf..c0525ee 100644
--- a/res/layout/all_apps_container.xml
+++ b/res/layout/all_apps_container.xml
@@ -27,7 +27,7 @@
     <!-- DO NOT CHANGE THE ID -->
     <com.android.launcher3.allapps.AllAppsRecyclerView
         android:id="@+id/apps_list_view"
-        android:theme="@style/Theme.Light.CustomOverscroll"
+        android:theme="@style/CustomOverscroll.Light"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center_horizontal|top"
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 1d593df..b7f76a6 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -51,7 +51,7 @@
 
     <HorizontalScrollView
         android:id="@+id/widgets_scroll_container"
-        android:theme="@style/Theme.Dark.CustomOverscroll"
+        android:theme="@style/CustomOverscroll.Dark"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:scrollbars="none">
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 755634f..cc513ab 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/CustomOverscroll.Dark"
+        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-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index b7a16f3..1fdb267 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -19,6 +19,14 @@
 
 <resources>
 
+    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowActionModeOverlay">true</item>
+    </style>
+
     <!-- Workspace -->
     <style name="DropTargetButtonContainer">
         <item name="android:layout_width">0dp</item>
diff --git a/res/values-v19/styles.xml b/res/values-v19/styles.xml
new file mode 100644
index 0000000..cfc7c0f
--- /dev/null
+++ b/res/values-v19/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2016 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.
+*/
+-->
+<resources>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
new file mode 100644
index 0000000..bc8523e
--- /dev/null
+++ b/res/values-v21/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2016 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.
+*/
+-->
+<resources>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+        <item name="android:statusBarColor">#00000000</item>
+        <item name="android:navigationBarColor">#00000000</item>
+        <item name="android:colorControlActivated">@color/launcher_accent_color</item>
+        <item name="android:colorAccent">@color/launcher_accent_color</item>
+        <item name="android:colorPrimary">@color/launcher_accent_color</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 55166cf..34d86df 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -43,6 +43,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/colors.xml b/res/values/colors.xml
index 8a7f627..b679270 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -37,6 +37,7 @@
     <color name="quantum_panel_bg_color_dark">#FF374248</color>
 
     <color name="outline_color">#FFFFFFFF</color>
+    <color name="launcher_accent_color">#ff009688</color>
 
     <!-- Containers -->
     <color name="container_fastscroll_thumb_inactive_color">#009688</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 4efffff..90df997 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -21,12 +21,12 @@
 
 <!-- AllApps & Launcher transitions -->
     <!-- The alpha of the AppsCustomize bg in spring loaded mode -->
-    <integer name="config_workspaceScrimAlpha">55</integer>
+    <integer name="config_workspaceScrimAlpha">30</integer>
     <integer name="config_allAppsTransitionTime">100</integer>
     <integer name="config_overviewTransitionTime">250</integer>
 
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
-    <integer name="config_workspaceSpringLoadShrinkPercentage">80</integer>
+    <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
     <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
     <integer name="config_workspaceOverviewShrinkPercentage">70</integer>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 37e4d04..468beaa 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -72,7 +72,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_icon_top_bottom_padding">8dp</dimen>
     <dimen name="all_apps_icon_width_gap">24dp</dimen>
     <!-- The top padding should account for the existing all_apps_list_top_bottom_padding -->
@@ -84,6 +84,19 @@
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
     <dimen name="all_apps_background_canvas_height">475dp</dimen>
 
+    <!-- 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_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 ecbdcc2..c4943f8 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/res/values/styles.xml b/res/values/styles.xml
index c03f08d..35a9088 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,14 +18,26 @@
 -->
 
 <resources>
-    <style name="Theme.Light.CustomOverscroll" parent="@android:style/Theme.DeviceDefault">
+    <!-- Launcher theme -->
+    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style>
+
+    <!-- Overscroll effect -->
+    <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault">
         <item name="android:colorEdgeEffect">@color/folder_edge_effect_color</item>
     </style>
 
-    <style name="Theme.Dark.CustomOverscroll" parent="@android:style/Theme.DeviceDefault">
+    <style name="CustomOverscroll.Dark" parent="@android:style/Theme.DeviceDefault">
         <item name="android:colorEdgeEffect">@color/workspace_edge_effect_color</item>
     </style>
 
+    <!-- Different icons -->
     <style name="Icon">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
@@ -56,6 +68,7 @@
         <item name="customShadows">false</item>
     </style>
 
+    <!-- Drop targets -->
     <style name="DropTargetButtonContainer">
         <item name="android:layout_width">0dp</item>
         <item name="android:layout_height">match_parent</item>
@@ -81,6 +94,7 @@
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
+    <!-- Virtual preloaders -->
     <style name="PreloadIcon">
         <item name="background">@drawable/virtual_preload</item>
         <item name="indicatorSize">4dp</item>
diff --git a/src/com/android/launcher3/AppInfoDropTargetBar.java b/src/com/android/launcher3/AppInfoDropTargetBar.java
index 99a1f41..e06f941 100644
--- a/src/com/android/launcher3/AppInfoDropTargetBar.java
+++ b/src/com/android/launcher3/AppInfoDropTargetBar.java
@@ -47,7 +47,6 @@
         dragController.addDragListener(this);
 
         dragController.addDragListener(mAppInfoDropTarget);
-
         dragController.addDropTarget(mAppInfoDropTarget);
 
         mAppInfoDropTarget.setLauncher(launcher);
@@ -64,7 +63,14 @@
     }
 
     private void animateDropTargetBarToAlpha(float alpha, int duration) {
-        animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, alpha,duration);
+        resetAnimation(duration);
+        if (duration > 0) {
+            animateAlpha(mDropTargetBar, alpha, DEFAULT_INTERPOLATOR);
+            mCurrentAnimation.start();
+        } else {
+            mDropTargetBar.setAlpha(alpha);
+            AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index e0946ea..5766e80 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -17,34 +17,40 @@
 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;
 
 import com.android.launcher3.config.ProviderConfig;
 
 /**
  * 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);
@@ -57,6 +63,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
@@ -65,10 +84,6 @@
         updateBackgroundAndPaddings();
     }
 
-    protected void setHasSearchBar() {
-        mHasSearchBar = true;
-    }
-
     /**
      * Sets the search bar bounds for this container view to match.
      */
@@ -94,47 +109,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.
@@ -144,4 +160,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/BaseDropTargetBar.java b/src/com/android/launcher3/BaseDropTargetBar.java
index 303acd7..9b38623 100644
--- a/src/com/android/launcher3/BaseDropTargetBar.java
+++ b/src/com/android/launcher3/BaseDropTargetBar.java
@@ -18,9 +18,13 @@
 
 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.util.AttributeSet;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 
@@ -31,14 +35,12 @@
  */
 public abstract class BaseDropTargetBar extends FrameLayout implements DragController.DragListener {
     protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
+    protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
 
     protected View mDropTargetBar;
-
-    protected LauncherViewPropertyAnimator mDropTargetBarAnimator;
-    protected static final AccelerateInterpolator sAccelerateInterpolator =
-            new AccelerateInterpolator();
     protected boolean mAccessibilityEnabled = false;
 
+    protected AnimatorSet mCurrentAnimation;
     protected boolean mDeferOnDragEnd;
 
     public BaseDropTargetBar(Context context, AttributeSet attrs) {
@@ -57,42 +59,35 @@
 
         // 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);
-                }
-            }
-        });
     }
 
-
     /**
-     * Convenience method to animate the alpha of a view using hardware layers.
+     * Convenience method to animate the alpha of a view.
      */
-    protected void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
-                                  int duration) {
-        if (v == null) {
-            return;
+    protected void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
+        if (Float.compare(v.getAlpha(), alpha) != 0) {
+            ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
+            anim.setInterpolator(interpolator);
+            anim.addListener(new ViewVisiblilyUpdateHandler(v));
+            mCurrentAnimation.play(anim);
+        }
+    }
+
+    protected void resetAnimation(int newAnimationDuration) {
+        // Update the accessibility state
+        AccessibilityManager am = (AccessibilityManager)
+                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mAccessibilityEnabled = am.isEnabled();
+
+        // Cancel any existing animation
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+            mCurrentAnimation = null;
         }
 
-        animator.cancel();
-        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);
-            }
+        if (newAnimationDuration > 0) {
+            mCurrentAnimation = new AnimatorSet();
+            mCurrentAnimation.setDuration(newAnimationDuration);
         }
     }
 
@@ -128,4 +123,24 @@
     public abstract void enableAccessibleDrag(boolean enable);
 
     public abstract void setup(Launcher launcher, DragController dragController);
+
+    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/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 40cdc80..a20a080 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -32,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.os.Parcelable;
@@ -45,7 +46,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.DecelerateInterpolator;
-import android.widget.Toast;
 
 import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
@@ -53,6 +53,7 @@
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.ParcelableSparseArray;
 import com.android.launcher3.util.Thunk;
@@ -211,7 +212,9 @@
         final Resources res = getResources();
         mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
 
-        mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel);
+        mBackground = (TransitionDrawable) res.getDrawable(
+                FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? R.drawable.bg_screenpanel
+                        : R.drawable.bg_celllayout);
         mBackground.setCallback(this);
         mBackground.setAlpha((int) (mBackgroundAlpha * 255));
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ce3e39c..ce01bad 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -33,6 +33,8 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
+import com.android.launcher3.config.FeatureFlags;
+
 public class DeviceProfile {
 
     public final InvariantDeviceProfile inv;
@@ -242,7 +244,8 @@
         FontMetrics fm = textPaint.getFontMetrics();
         cellWidthPx = iconSizePx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
-        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
+        final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
+                : res.getDimensionPixelSize(R.dimen.dragViewScale);
         dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
 
         // Hotseat
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index c421815..850e326 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -302,7 +302,7 @@
         }
 
         mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
-            IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+                IMPORTANT_FOR_ACCESSIBILITY_AUTO);
         mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
     }
 
@@ -981,7 +981,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return Workspace.IS_SPRING_LOADED;
+        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3f0e85a..414ebb4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -96,11 +96,13 @@
 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;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -111,6 +113,7 @@
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.util.WallpaperUtils;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetsContainerView;
@@ -1375,11 +1378,14 @@
         // Setup Apps and Widgets
         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
         mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
-        if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
-            mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
-        } else {
-            mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
-        }
+        mAppsView.setSearchBarController(new DefaultAppSearchController());
+
+        // TODO: Reenable this
+//        if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
+//            mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
+//        } else {
+//            mAppsView.setSearchBarController(new DefaultAppSearchController());
+//        }
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         dragController.setDragScoller(mWorkspace);
@@ -2762,7 +2768,7 @@
         int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
         float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
         startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName())
-                        .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET, offset),
+                        .putExtra(WallpaperUtils.EXTRA_WALLPAPER_OFFSET, offset),
                 REQUEST_PICK_WALLPAPER);
 
         if (mLauncherCallbacks != null) {
@@ -3476,7 +3482,7 @@
             mState = State.APPS_SPRING_LOADED;
         } else if (isWidgetsViewVisible()) {
             mState = State.WIDGETS_SPRING_LOADED;
-        } else if (Workspace.IS_SPRING_LOADED) {
+        } else if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
             mState = State.WORKSPACE_SPRING_LOADED;
         } else {
             mState = State.WORKSPACE;
@@ -3505,7 +3511,7 @@
         }, delay);
     }
 
-    private boolean isStateSpringLoaded() {
+    boolean isStateSpringLoaded() {
         return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED
                 || mState == State.WIDGETS_SPRING_LOADED;
     }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 96da183..c789082 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -61,6 +61,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
@@ -245,6 +247,7 @@
     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         if (gainFocus) {
             mChildrenFocused = false;
+            dispatchChildFocus(false);
         }
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
     }
@@ -253,6 +256,9 @@
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
         dispatchChildFocus(focused != null);
+        if (focused != null) {
+            focused.setFocusableInTouchMode(false);
+        }
     }
 
     @Override
@@ -266,10 +272,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/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index be74520..d8bc631 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -138,6 +138,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
@@ -241,6 +243,7 @@
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
+        mFgWidgetsModel = mBgWidgetsModel.clone();
         mIconCache = iconCache;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
@@ -2684,18 +2687,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");
                     }
                 }
             };
@@ -3336,20 +3338,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());
             }
         });
     }
@@ -3360,6 +3360,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));
@@ -3387,6 +3388,7 @@
             }
         }
         mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
+        mFgWidgetsModel = mBgWidgetsModel.clone();
     }
 
     @Thunk static boolean isPackageDisabled(Context context, String packageName,
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 668e021..54945be 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -82,13 +82,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;
         }
@@ -99,7 +104,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;
@@ -123,11 +128,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;
@@ -154,8 +155,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);
     }
 
     /**
@@ -165,15 +165,9 @@
             final boolean animated) {
         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));
     }
 
     /**
@@ -205,16 +199,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();
 
@@ -226,11 +219,15 @@
         // Cancel the current animation
         cancelAnimation();
 
-        playCommonTransitionAnimations(fromWorkspaceState, toWorkspaceState, fromView, toView,
-                overlaySearchBarView, animated, initialized, animation, revealDuration, layerViews);
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, revealDuration, layerViews);
+
+        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);
@@ -244,9 +241,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 {
@@ -271,15 +268,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);
@@ -398,8 +386,8 @@
     /**
      * Plays animations used by various transitions.
      */
-    private void playCommonTransitionAnimations(Workspace.State fromWorkspaceState,
-            Workspace.State toWorkspaceState, View fromView, View toView, View overlaySearchBarView,
+    private void playCommonTransitionAnimations(
+            Workspace.State toWorkspaceState, View fromView, View toView,
             boolean animated, boolean initialized, AnimatorSet animation, int revealDuration,
             HashMap<View, Integer> layerViews) {
         // Create the workspace animation.
@@ -408,8 +396,10 @@
                 animated, layerViews);
 
         // Animate the search bar
-        startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
-                animated ? revealDuration : 0, overlaySearchBarView);
+        final SearchDropTargetBar.State toSearchBarState =
+                toWorkspaceState.getSearchDropTargetBarState();
+        mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState,
+                animated ? revealDuration : 0, animation);
 
         if (animated && initialized) {
             // Play the workspace animation
@@ -446,12 +436,8 @@
             final Workspace.State toWorkspaceState, 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;
@@ -479,9 +465,8 @@
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
-                mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
-                appsView.getRevealView(), appsView.getSearchBarView(), animated,
-                onCompleteRunnable, cb);
+                mLauncher.getAllAppsButton(), appsView,
+                animated, onCompleteRunnable, cb);
     }
 
     /**
@@ -491,11 +476,8 @@
             final Workspace.State toWorkspaceState, 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) {
@@ -507,10 +489,10 @@
                 };
             }
         };
-        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
-                toWorkspaceState, mLauncher.getWidgetsButton(), widgetsView,
-                widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
-                onCompleteRunnable, cb);
+        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
+                fromWorkspaceState, toWorkspaceState,
+                mLauncher.getWidgetsButton(), widgetsView,
+                animated, onCompleteRunnable, cb);
     }
 
     /**
@@ -528,8 +510,8 @@
         // Cancel the current animation
         cancelAnimation();
 
-        playCommonTransitionAnimations(fromWorkspaceState, toWorkspaceState, fromWorkspace, null,
-                null, animated, animated, animation, revealDuration, layerViews);
+        playCommonTransitionAnimations(toWorkspaceState, fromWorkspace, null,
+                animated, animated, animation, revealDuration, layerViews);
 
         if (animated) {
             dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, true);
@@ -597,10 +579,10 @@
     /**
      * Creates and starts a new animation to the workspace.
      */
-    private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
-            final Workspace.State toWorkspaceState, 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 View buttonView, final BaseContainerView fromView,
+            final boolean animated, final Runnable onCompleteRunnable,
             final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
@@ -619,10 +601,13 @@
         // Cancel the current animation
         cancelAnimation();
 
-        playCommonTransitionAnimations(fromWorkspaceState, toWorkspaceState, fromView, toView,
-                overlaySearchBarView, animated, initialized, animation, revealDuration, layerViews);
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, revealDuration, layerViews);
 
         if (animated && initialized) {
+            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
             // the all-apps button to pop in and out.
@@ -670,7 +655,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);
@@ -698,16 +683,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();
@@ -753,9 +728,6 @@
                         contentView.setTranslationY(0);
                         contentView.setAlpha(1);
                     }
-                    if (overlaySearchBarView != null) {
-                        overlaySearchBarView.setAlpha(1f);
-                    }
 
                     // This can hold unnecessary references to views.
                     cleanupAnimation();
@@ -810,41 +782,6 @@
     }
 
     /**
-     * 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) {
-        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);
-    }
-
-    /**
      * Dispatches the prepare-transition event to suitable views.
      */
     void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index fed972c..171dd87 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -18,12 +18,17 @@
 
 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.view.View;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.util.Thunk;
@@ -34,32 +39,29 @@
  */
 public class SearchDropTargetBar extends BaseDropTargetBar {
 
+    private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
+    private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
+
     /** 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 LauncherViewPropertyAnimator mQSBSearchBarAnimator;
-
     @ViewDebug.ExportedProperty(category = "launcher")
     private State mState = State.SEARCH_BAR;
     @Thunk View mQSB;
@@ -116,29 +118,6 @@
 
     public void setQsbSearchBar(View qsb) {
         mQSB = qsb;
-        if (mQSB != null) {
-            // Update the search bar 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;
-        }
     }
 
     /**
@@ -146,18 +125,53 @@
      * 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;
 
-            // Update the accessibility state
-            AccessibilityManager am = (AccessibilityManager)
-                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-            mAccessibilityEnabled = am.isEnabled();
+            resetAnimation(duration);
+            if (duration > 0) {
+                animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
+            } else {
+                mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
+                AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+            }
 
-            animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
-                    duration);
-            animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
-                    duration);
+            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();
+                }
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 71c5234..00854fa 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -62,6 +62,7 @@
 import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -91,7 +92,6 @@
         Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
     private static final String TAG = "Launcher.Workspace";
 
-    public static final boolean IS_SPRING_LOADED = true;
     private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
 
     private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
@@ -174,7 +174,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);
@@ -404,11 +404,6 @@
         mLauncher.onInteractionEnd();
     }
 
-    /** Returns a scale factor to apply to workspace icons when dragging them from the workspace. */
-    public float getDragShrinkFactor() {
-        return IS_SPRING_LOADED ? mSpringLoadedShrinkFactor : 1f;
-    }
-
     /**
      * Initializes various states for this workspace.
      */
@@ -1799,7 +1794,14 @@
     }
 
     int getSpringLoadedTranslationY() {
-        return getOverviewModeTranslationY();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
+        int scaledHeight = (int) (mSpringLoadedShrinkFactor * getNormalChildHeight());
+        int workspaceTop = mInsets.top + workspacePadding.top;
+        int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
+        int workspaceHeight = workspaceBottom - workspaceTop;
+        // Center the spring-loaded pages by translating it up by half of the reduced height.
+        return -(workspaceHeight - scaledHeight) / 2;
     }
 
     /**
@@ -2147,7 +2149,7 @@
 
         b.recycle();
 
-        if (IS_SPRING_LOADED) {
+        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
             mLauncher.enterSpringLoadedDragMode();
         }
     }
@@ -2199,7 +2201,7 @@
         // Recycle temporary bitmaps
         tmpB.recycle();
 
-        if (IS_SPRING_LOADED) {
+        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
             mLauncher.enterSpringLoadedDragMode();
         }
     }
@@ -2641,7 +2643,7 @@
         setCurrentDropLayout(layout);
         setCurrentDragOverlappingLayout(layout);
 
-        if (!workspaceInModalState() && !IS_SPRING_LOADED) {
+        if (!workspaceInModalState() && FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
             mLauncher.getDragLayer().showPageHints();
         }
     }
@@ -3563,7 +3565,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return IS_SPRING_LOADED;
+        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
     }
 
     @Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index d32ce73..c0eb7ed 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -198,6 +198,7 @@
     @Thunk int mAllAppsTransitionTime;
     @Thunk int mOverviewTransitionTime;
     @Thunk int mOverlayTransitionTime;
+    @Thunk int mSpringLoadedTransitionTime;
     @Thunk boolean mWorkspaceFadeInAdjacentScreens;
 
     public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
@@ -209,6 +210,7 @@
         mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime);
         mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
         mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
+        mSpringLoadedTransitionTime = mOverlayTransitionTime / 2;
         mSpringLoadedShrinkFactor =
                 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
         mOverviewModeShrinkFactor =
@@ -259,6 +261,9 @@
             return mAllAppsTransitionTime;
         } else if (states.workspaceToOverview || states.overviewToWorkspace) {
             return mOverviewTransitionTime;
+        } else if (mLauncher.mState == Launcher.State.WORKSPACE_SPRING_LOADED
+                || states.oldStateIsNormal && states.stateIsSpringLoaded) {
+            return mSpringLoadedTransitionTime;
         } else {
             return mOverlayTransitionTime;
         }
@@ -282,8 +287,8 @@
         // Update the workspace state
         float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
                 1.0f : 0f;
-        float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ?
-                1f : 0f;
+        float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ? 1f : 0f;
+        float finalPageIndicatorAlpha = states.stateIsNormal ? 1f : 0f;
         float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
 
         float finalWorkspaceTranslationY = 0;
@@ -393,7 +398,7 @@
             Animator pageIndicatorAlpha;
             if (pageIndicator != null) {
                 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
-                        .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
+                        .alpha(finalPageIndicatorAlpha).withLayer();
                 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator,
                         accessibilityEnabled));
             } else {
@@ -402,7 +407,7 @@
             }
 
             LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
-                    .alpha(finalHotseatAndPageIndicatorAlpha);
+                    .alpha(finalHotseatAlpha);
             hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled));
 
             LauncherViewPropertyAnimator overviewPanelAlpha =
@@ -456,10 +461,10 @@
         } else {
             overviewPanel.setAlpha(finalOverviewPanelAlpha);
             AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
-            hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
+            hotseat.setAlpha(finalHotseatAlpha);
             AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled);
             if (pageIndicator != null) {
-                pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
+                pageIndicator.setAlpha(finalPageIndicatorAlpha);
                 AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled);
             }
             mWorkspace.updateCustomContentVisibility();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 84807cb..6a9d910 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
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 d853d5b..fdce389 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -15,64 +15,179 @@
  */
 package com.android.launcher3.allapps;
 
+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/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/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 5012708..b0b602c 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -24,7 +24,6 @@
  * Use LAUNCHER3_ prefix for prevent namespace conflicts.
  */
 public final class FeatureFlags {
-
     private FeatureFlags() {}
 
     public static boolean IS_DEV_BUILD = false;
@@ -33,5 +32,6 @@
 
     // Custom flags go below this
     public static boolean LAUNCHER3_ICON_NORMALIZATION = false;
-
+    // As opposed to the new spring-loaded workspace.
+    public static boolean LAUNCHER3_LEGACY_WORKSPACE_DND = false;
 }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 704da0e..80f9e46 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.util.Thunk;
 
@@ -245,10 +246,9 @@
 
         mDragObject = new DropTarget.DragObject();
 
-        float finalDragViewScale = mLauncher.getWorkspace().getDragShrinkFactor();
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                 registrationY, 0, 0, b.getWidth(), b.getHeight(),
-                initialDragViewScale, finalDragViewScale);
+                initialDragViewScale);
 
         mDragObject.dragComplete = false;
         if (mIsAccessibleDrag) {
@@ -572,7 +572,7 @@
             if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
-                    dragLayer.onEnterScrollArea(forwardDirection);
+                    dragLayer.onEnterScrollArea();
                     mScrollRunnable.setDirection(forwardDirection);
                     mHandler.postDelayed(mScrollRunnable, delay);
                 }
@@ -581,7 +581,7 @@
             if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
-                    dragLayer.onEnterScrollArea(backwardsDirection);
+                    dragLayer.onEnterScrollArea();
                     mScrollRunnable.setDirection(backwardsDirection);
                     mHandler.postDelayed(mScrollRunnable, delay);
                 }
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index ded7fae..1ad8e95 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -897,7 +897,7 @@
         }
     }
 
-    void onEnterScrollArea(int direction) {
+    void onEnterScrollArea() {
         mInScrollArea = true;
         invalidate();
     }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 01189d0..b1df41b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Thunk;
 
 import com.android.launcher3.R;
@@ -84,15 +85,15 @@
      */
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
-            int left, int top, int width, int height, final float initialScale,
-            float finalDragViewScale) {
+            int left, int top, int width, int height, final float initialScale) {
         super(launcher);
         mDragLayer = launcher.getDragLayer();
         mDragController = launcher.getDragController();
 
         final Resources res = getResources();
-        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
-        final float scale = finalDragViewScale * (width + scaleDps) / width;
+        final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
+                : res.getDimensionPixelSize(R.dimen.dragViewScale);
+        final float scale = (width + scaleDps) / width;
 
         // Set the initial scale to avoid any jumps
         setScaleX(initialScale);
diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java
index a5251e1..45c83bd 100644
--- a/src/com/android/launcher3/util/WallpaperUtils.java
+++ b/src/com/android/launcher3/util/WallpaperUtils.java
@@ -38,6 +38,8 @@
     public static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
     public static final float WALLPAPER_SCREENS_SPAN = 2f;
 
+    public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
+
     public static void saveWallpaperDimensions(int width, int height, Activity activity) {
         if (Utilities.ATLEAST_KITKAT) {
             // From Kitkat onwards, ImageWallpaper does not care about the
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index a1472cc..db31756 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);
     }
@@ -340,21 +329,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);
     }