Merge "Better separation between DragDriver and DragController" into ub-launcher3-master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ab430d1..ce6fb21 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -52,7 +52,6 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
-    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml
index 9bec86a..060a1f8 100644
--- a/res/xml/default_workspace_4x4.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -15,102 +15,33 @@
 -->
 
 <favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-    <!-- Far-left screen [0] -->
 
-    <!-- Left screen [1] -->
-    <appwidget
-        launcher:packageName="com.android.settings"
-        launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
-        launcher:screen="1"
-        launcher:x="0"
-        launcher:y="3"
-        launcher:spanX="4"
-        launcher:spanY="1" />
+    <!-- Hotseat -->
+    <include launcher:workspace="@xml/dw_phone_hotseat" />
 
-    <!-- Middle screen [2] -->
-    <appwidget
-        launcher:packageName="com.android.deskclock"
-        launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
-        launcher:screen="2"
-        launcher:x="1"
-        launcher:y="0"
-        launcher:spanX="2"
-        launcher:spanY="2" />
-    <favorite
-        launcher:packageName="com.android.camera"
-        launcher:className="com.android.camera.Camera"
-        launcher:screen="2"
-        launcher:x="0"
-        launcher:y="3" />
-
-    <!-- Right screen [3] -->
-    <favorite
-        launcher:packageName="com.android.gallery3d"
-        launcher:className="com.android.gallery3d.app.Gallery"
-        launcher:screen="3"
-        launcher:x="1"
-        launcher:y="3" />
-    <favorite
-        launcher:packageName="com.android.settings"
-        launcher:className="com.android.settings.Settings"
-        launcher:screen="3"
-        launcher:x="2"
-        launcher:y="3" />
-
-    <!-- Far-right screen [4] -->
-
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+    <!-- Bottom row -->
     <resolve
-        launcher:container="-101"
         launcher:screen="0"
         launcher:x="0"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
-        <favorite launcher:uri="tel:123" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
-
-        <favorite
-            launcher:packageName="com.android.dialer"
-            launcher:className="com.android.dialer.DialtactsActivity" />
+        launcher:y="3" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+        <favorite launcher:uri="mailto:" />
     </resolve>
 
-    <favorite
-        launcher:packageName="com.android.contacts"
-        launcher:className="com.android.contacts.activities.PeopleActivity"
-        launcher:container="-101"
-        launcher:screen="1"
+    <resolve
+        launcher:screen="0"
         launcher:x="1"
-        launcher:y="0" />
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="3"
-        launcher:x="3"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
-        <favorite launcher:uri="sms:" />
-        <favorite launcher:uri="smsto:" />
-        <favorite launcher:uri="mms:" />
-        <favorite launcher:uri="mmsto:" />
-
-        <favorite
-            launcher:packageName="com.android.mms"
-            launcher:className="com.android.mms.ui.ConversationList" />
+        launcher:y="3" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+        <favorite launcher:uri="#Intent;type=images/*;end" />
     </resolve>
-    <resolve
-        launcher:container="-101"
-        launcher:screen="4"
-        launcher:x="4"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-        <favorite launcher:uri="http://www.example.com/" />
 
-        <favorite
-            launcher:packageName="com.android.browser"
-            launcher:className="com.android.browser.BrowserActivity" />
+    <resolve
+        launcher:screen="0"
+        launcher:x="3"
+        launcher:y="3" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
+        <favorite launcher:uri="market://details?id=com.android.launcher" />
     </resolve>
 
 </favorites>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index 9bec86a..3226617 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -15,102 +15,34 @@
 -->
 
 <favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-    <!-- Far-left screen [0] -->
 
-    <!-- Left screen [1] -->
-    <appwidget
-        launcher:packageName="com.android.settings"
-        launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
-        launcher:screen="1"
-        launcher:x="0"
-        launcher:y="3"
-        launcher:spanX="4"
-        launcher:spanY="1" />
+    <!-- Hotseat -->
+    <include launcher:workspace="@xml/dw_phone_hotseat" />
 
-    <!-- Middle screen [2] -->
-    <appwidget
-        launcher:packageName="com.android.deskclock"
-        launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
-        launcher:screen="2"
-        launcher:x="1"
-        launcher:y="0"
-        launcher:spanX="2"
-        launcher:spanY="2" />
-    <favorite
-        launcher:packageName="com.android.camera"
-        launcher:className="com.android.camera.Camera"
-        launcher:screen="2"
-        launcher:x="0"
-        launcher:y="3" />
-
-    <!-- Right screen [3] -->
-    <favorite
-        launcher:packageName="com.android.gallery3d"
-        launcher:className="com.android.gallery3d.app.Gallery"
-        launcher:screen="3"
-        launcher:x="1"
-        launcher:y="3" />
-    <favorite
-        launcher:packageName="com.android.settings"
-        launcher:className="com.android.settings.Settings"
-        launcher:screen="3"
-        launcher:x="2"
-        launcher:y="3" />
-
-    <!-- Far-right screen [4] -->
-
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+    <!-- Bottom row -->
     <resolve
-        launcher:container="-101"
         launcher:screen="0"
         launcher:x="0"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
-        <favorite launcher:uri="tel:123" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+        launcher:y="4" >
+	    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+	    <favorite launcher:uri="mailto:" />
 
-        <favorite
-            launcher:packageName="com.android.dialer"
-            launcher:className="com.android.dialer.DialtactsActivity" />
     </resolve>
 
-    <favorite
-        launcher:packageName="com.android.contacts"
-        launcher:className="com.android.contacts.activities.PeopleActivity"
-        launcher:container="-101"
-        launcher:screen="1"
+    <resolve
+        launcher:screen="0"
         launcher:x="1"
-        launcher:y="0" />
+        launcher:y="4" >
+	    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+	    <favorite launcher:uri="#Intent;type=images/*;end" />
 
-    <resolve
-        launcher:container="-101"
-        launcher:screen="3"
-        launcher:x="3"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
-        <favorite launcher:uri="sms:" />
-        <favorite launcher:uri="smsto:" />
-        <favorite launcher:uri="mms:" />
-        <favorite launcher:uri="mmsto:" />
-
-        <favorite
-            launcher:packageName="com.android.mms"
-            launcher:className="com.android.mms.ui.ConversationList" />
     </resolve>
+
     <resolve
-        launcher:container="-101"
-        launcher:screen="4"
+        launcher:screen="0"
         launcher:x="4"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-        <favorite launcher:uri="http://www.example.com/" />
-
-        <favorite
-            launcher:packageName="com.android.browser"
-            launcher:className="com.android.browser.BrowserActivity" />
+        launcher:y="4" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
+        <favorite launcher:uri="market://details?id=com.android.launcher" />
     </resolve>
-
 </favorites>
diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml
index d42a93a..bc236fb 100644
--- a/res/xml/default_workspace_5x6.xml
+++ b/res/xml/default_workspace_5x6.xml
@@ -15,101 +15,23 @@
 -->
 
 <favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-    <!-- Far-left screen [0] -->
 
-    <!-- Left screen [1] -->
-    <appwidget
-        launcher:packageName="com.android.settings"
-        launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
-        launcher:screen="1"
+    <!-- Hotseat -->
+    <include launcher:workspace="@xml/dw_tablet_hotseat" />
+
+    <!-- Bottom row -->
+    <favorite
+        launcher:screen="0"
         launcher:x="0"
-        launcher:y="3"
-        launcher:spanX="4"
-        launcher:spanY="1" />
-
-    <!-- Middle screen [2] -->
-    <appwidget
-        launcher:packageName="com.android.deskclock"
-        launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
-        launcher:screen="2"
-        launcher:x="1"
-        launcher:y="0"
-        launcher:spanX="2"
-        launcher:spanY="2" />
-    <favorite
-        launcher:packageName="com.android.camera"
-        launcher:className="com.android.camera.Camera"
-        launcher:screen="2"
-        launcher:x="0"
-        launcher:y="3" />
-
-    <!-- Right screen [3] -->
-    <favorite
-        launcher:packageName="com.android.gallery3d"
-        launcher:className="com.android.gallery3d.app.Gallery"
-        launcher:screen="3"
-        launcher:x="1"
-        launcher:y="3" />
-    <favorite
-        launcher:packageName="com.android.settings"
-        launcher:className="com.android.settings.Settings"
-        launcher:screen="3"
-        launcher:x="2"
-        launcher:y="3" />
-
-    <!-- Far-right screen [4] -->
-
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
-    <resolve
-        launcher:container="-101"
-        launcher:screen="1"
-        launcher:x="1"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
-        <favorite launcher:uri="tel:123" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
-
-        <favorite
-            launcher:packageName="com.android.dialer"
-            launcher:className="com.android.dialer.DialtactsActivity" />
-    </resolve>
-
-    <favorite
-        launcher:packageName="com.android.contacts"
-        launcher:className="com.android.contacts.activities.PeopleActivity"
-        launcher:container="-101"
-        launcher:screen="2"
-        launcher:x="2"
-        launcher:y="0" />
+        launcher:y="4"
+        launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CONTACTS;end" />
 
     <resolve
-        launcher:container="-101"
-        launcher:screen="4"
-        launcher:x="4"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
-        <favorite launcher:uri="sms:" />
-        <favorite launcher:uri="smsto:" />
-        <favorite launcher:uri="mms:" />
-        <favorite launcher:uri="mmsto:" />
-
-        <favorite
-            launcher:packageName="com.android.mms"
-            launcher:className="com.android.mms.ui.ConversationList" />
-    </resolve>
-    <resolve
-        launcher:container="-101"
-        launcher:screen="5"
+        launcher:screen="0"
         launcher:x="5"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-        <favorite launcher:uri="http://www.example.com/" />
-
-        <favorite
-            launcher:packageName="com.android.browser"
-            launcher:className="com.android.browser.BrowserActivity" />
+        launcher:y="4" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
+        <favorite launcher:uri="market://details?id=com.android.launcher" />
     </resolve>
+
 </favorites>
diff --git a/res/xml/dw_phone_hotseat.xml b/res/xml/dw_phone_hotseat.xml
new file mode 100644
index 0000000..b58994d
--- /dev/null
+++ b/res/xml/dw_phone_hotseat.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Dialer, Messaging, [All Apps], Browser, Camera -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+        <favorite launcher:uri="tel:123" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+        <favorite launcher:uri="sms:" />
+        <favorite launcher:uri="smsto:" />
+        <favorite launcher:uri="mms:" />
+        <favorite launcher:uri="mmsto:" />
+    </resolve>
+
+    <!-- All Apps -->
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
+
+</favorites>
diff --git a/res/xml/dw_tablet_hotseat.xml b/res/xml/dw_tablet_hotseat.xml
new file mode 100644
index 0000000..671ccba
--- /dev/null
+++ b/res/xml/dw_tablet_hotseat.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Messaging, Email, Browser, [All Apps], Music, Gallery, Camera -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+        <favorite launcher:uri="sms:" />
+        <favorite launcher:uri="smsto:" />
+        <favorite launcher:uri="mms:" />
+        <favorite launcher:uri="mmsto:" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+        <favorite launcher:uri="mailto:" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+    </resolve>
+
+    <!-- All Apps -->
+
+    <favorite
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0"
+        launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="5"
+        launcher:x="5"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+        <favorite launcher:uri="#Intent;type=images/*;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="6"
+        launcher:x="6"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
+
+</favorites>
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 99a98dd..440e4e7 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -355,7 +355,7 @@
                     return addShortcut(info.loadLabel(mPackageManager).toString(),
                             intent, Favorites.ITEM_TYPE_APPLICATION);
                 } catch (PackageManager.NameNotFoundException e) {
-                    if (LOGD) Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
+                    Log.e(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
                 }
                 return -1;
             } else {
@@ -367,7 +367,7 @@
          * Helper method to allow extending the parser capabilities
          */
         protected long invalidPackageOrClass(XmlResourceParser parser) {
-            if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+            Log.w(TAG, "Skipping invalid <favorite> with no component");
             return -1;
         }
     }
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index a049ec0..e0946ea 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -36,10 +36,12 @@
     // 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 bounds of the container
+    // The computed bounds of the search bar
+    private Rect mSearchBarBounds = new Rect();
+    // The computed bounds of the container
     protected Rect mContentBounds = new Rect();
-    // The padding to apply to the container to achieve the bounds
-    protected Rect mContentPadding = new Rect();
+    // The computed padding to apply to the container to achieve the container bounds
+    private 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;
@@ -92,7 +94,7 @@
      */
     protected void updateBackgroundAndPaddings() {
         Rect padding;
-        Rect searchBarBounds = new Rect(mFixedSearchBarBounds);
+        Rect searchBarBounds = new Rect();
         if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
             // Use the default bounds
             padding = new Rect(mInsets.left + mContainerBoundsInset,
@@ -112,14 +114,20 @@
                     (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
                     getMeasuredWidth() - mFixedSearchBarBounds.right,
                     mInsets.bottom + mContainerBoundsInset);
+
+            // Use the search bounds
+            searchBarBounds.set(mFixedSearchBarBounds);
         }
-        if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mFixedSearchBarBounds)) {
+
+        // 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)) {
             mContentPadding.set(padding);
             mContentBounds.set(padding.left, padding.top,
                     getMeasuredWidth() - padding.right,
                     getMeasuredHeight() - padding.bottom);
-            mFixedSearchBarBounds.set(searchBarBounds);
-            onUpdateBackgroundAndPaddings(mFixedSearchBarBounds, padding);
+            mSearchBarBounds.set(searchBarBounds);
+            onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
         }
     }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 6cfdcf7..9b4642d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -25,7 +25,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
-import android.util.Size;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -385,7 +384,7 @@
         final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
 
         // Layout the search bar space
-        View searchBar = launcher.getSearchBar();
+        View searchBar = launcher.getSearchDropTargetBar();
         lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
         if (hasVerticalBarLayout) {
             // Vertical search bar space -- The search bar is fixed in the layout to be on the left
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 8445cca..f523c49 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -160,7 +160,7 @@
     }
 
     private boolean isEventOverDropTargetBar(MotionEvent ev) {
-        getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect);
+        getDescendantRectRelativeToSelf(mLauncher.getSearchDropTargetBar(), mHitRect);
         if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
             return true;
         }
@@ -322,7 +322,7 @@
             childrenForAccessibility.add(currentFolder);
 
             if (isInAccessibleDrag()) {
-                childrenForAccessibility.add(mLauncher.getSearchBar());
+                childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
             }
         } else {
             super.addChildrenForAccessibility(childrenForAccessibility);
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index fc59546..7911735 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -275,7 +275,7 @@
 
     @Override
     public void enableAccessibleDrag(boolean enable) {
-        mLauncher.getSearchBar().enableAccessibleDrag(enable);
+        mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
         for (int i = 0; i < mContent.getChildCount(); i++) {
             mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
         }
diff --git a/src/com/android/launcher3/FolderEditText.java b/src/com/android/launcher3/FolderEditText.java
index c311008..c38307c 100644
--- a/src/com/android/launcher3/FolderEditText.java
+++ b/src/com/android/launcher3/FolderEditText.java
@@ -2,6 +2,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.widget.EditText;
 
@@ -33,4 +34,10 @@
         }
         return super.onKeyPreIme(keyCode, event);
     }
+
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        // We don't want this view to interfere with Launcher own drag and drop.
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c5e49e6..1909e7d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -132,8 +132,7 @@
  */
 public class Launcher extends Activity
         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
-                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
-                   LauncherStateTransitionAnimation.Callbacks {
+                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
     static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -448,7 +447,7 @@
 
         mDragController = new DragController(this);
         mInflater = getLayoutInflater();
-        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this);
+        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
 
         mStats = new Stats(this);
 
@@ -739,6 +738,7 @@
         };
 
         if (requestCode == REQUEST_BIND_APPWIDGET) {
+            // This is called only if the user did not previously have permissions to bind widgets
             final int appWidgetId = data != null ?
                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
             if (resultCode == RESULT_CANCELED) {
@@ -748,6 +748,10 @@
             } else if (resultCode == RESULT_OK) {
                 addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
                         mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
+
+                // When the user has granted permission to bind widgets, we should check to see if
+                // we can inflate the default search bar widget.
+                getOrCreateQsbBar();
             }
             return;
         } else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -1816,7 +1820,7 @@
         return mOverviewPanel;
     }
 
-    public SearchDropTargetBar getSearchBar() {
+    public SearchDropTargetBar getSearchDropTargetBar() {
         return mSearchDropTargetBar;
     }
 
@@ -3228,14 +3232,6 @@
         }
     }
 
-    @Override
-    public void onStateTransitionHideSearchBar() {
-        // Hide the search bar
-        if (mSearchDropTargetBar != null) {
-            mSearchDropTargetBar.hideSearchBar(false /* animated */);
-        }
-    }
-
     public void showWorkspace(boolean animated) {
         showWorkspace(animated, null);
     }
@@ -3244,16 +3240,9 @@
         boolean changed = mState != State.WORKSPACE ||
                 mWorkspace.getState() != Workspace.State.NORMAL;
         if (changed) {
-            boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
             mWorkspace.setVisibility(View.VISIBLE);
-            mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL,
-                    animated, onCompleteRunnable);
-
-            // Show the search bar (only animate if we were showing the drop target bar in spring
-            // loaded mode)
-            if (mSearchDropTargetBar != null) {
-                mSearchDropTargetBar.showSearchBar(animated && wasInSpringLoadedMode);
-            }
+            mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
+                    Workspace.State.NORMAL, animated, onCompleteRunnable);
 
             // Set focus to the AppsCustomize button
             if (mAllAppsButton != null) {
@@ -3277,8 +3266,8 @@
 
     void showOverviewMode(boolean animated) {
         mWorkspace.setVisibility(View.VISIBLE);
-        mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW,
-                animated, null /* onCompleteRunnable */);
+        mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
+                Workspace.State.OVERVIEW, animated, null /* onCompleteRunnable */);
         mState = State.WORKSPACE;
     }
 
@@ -3331,9 +3320,10 @@
         }
 
         if (toState == State.APPS) {
-            mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
+            mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated,
+                    focusSearchBar);
         } else {
-            mStateTransitionAnimation.startAnimationToWidgets(animated);
+            mStateTransitionAnimation.startAnimationToWidgets(mWorkspace.getState(), animated);
         }
 
         // Change the state *after* we've called all the transition code
@@ -3355,14 +3345,25 @@
      * new state.
      */
     public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
-            boolean animated, boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
+            boolean animated, HashMap<View, Integer> layerViews) {
         Workspace.State fromState = mWorkspace.getState();
-        Animator anim = mWorkspace.setStateWithAnimation(
-                toState, animated, hasOverlaySearchBar, layerViews);
+        Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
         updateInteraction(fromState, toState);
         return anim;
     }
 
+    public void onLauncherClingShown() {
+        // When a launcher cling appears, it should cover the underlying layers, so their focus
+        // should be blocked.
+        if (mDragLayer.getDescendantFocusability() != ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
+            mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        }
+    }
+
+    public void onLauncherClingDismissed() {
+        mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+    }
+
     public void enterSpringLoadedDragMode() {
         if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
         if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
@@ -3370,8 +3371,9 @@
             return;
         }
 
-        mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED,
-                true /* animated */, null /* onCompleteRunnable */);
+        mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
+                Workspace.State.SPRING_LOADED, true /* animated */,
+                null /* onCompleteRunnable */);
         mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
     }
 
@@ -4468,14 +4470,16 @@
         if (mWorkspace != null) mWorkspace.setAlpha(1f);
         if (mHotseat != null) mHotseat.setAlpha(1f);
         if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
-        if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
+        if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
+                SearchDropTargetBar.State.SEARCH_BAR, 0);
     }
 
     void hideWorkspaceSearchAndHotseat() {
         if (mWorkspace != null) mWorkspace.setAlpha(0f);
         if (mHotseat != null) mHotseat.setAlpha(0f);
         if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
-        if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
+        if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
+                SearchDropTargetBar.State.INVISIBLE, 0);
     }
 
     // TODO: These method should be a part of LauncherSearchCallback
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 37b8a79..f261387 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -20,11 +20,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.UserManager;
 import android.util.Log;
 
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.Thunk;
 
 import java.lang.ref.WeakReference;
@@ -98,6 +100,7 @@
         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
 
         sContext.registerReceiver(mModel, filter);
+        UserManagerCompat.getInstance(sContext).enableAndResetCache();
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 6eb3ac8..0315b5c 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.annotation.TargetApi;
@@ -29,23 +27,24 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.accessibility.AccessibilityManager;
+
 import com.android.launcher3.util.Thunk;
 
-class LauncherClings implements OnClickListener {
+class LauncherClings implements OnClickListener, OnKeyListener {
     private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
     private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
 
     private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
 
-    private static final boolean DISABLE_CLINGS = false;
-
     private static final int SHOW_CLING_DURATION = 250;
     private static final int DISMISS_CLING_DURATION = 200;
 
@@ -87,6 +86,20 @@
         }
     }
 
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        if (event.isPrintingKey()) {
+            // Should ignore all printing keys, otherwise they come to the search box.
+            return true;
+        }
+        if (keyCode == KeyEvent.KEYCODE_MENU) {
+            // Menu key goes to the overview mode similar to longpress, therefore it needs to
+            // dismiss the clings.
+            dismissLongPressCling();
+        }
+        return false;
+    }
+
     /**
      * Shows the migration cling.
      *
@@ -94,6 +107,7 @@
      * package was not preinstalled and there exists a db to migrate from.
      */
     public void showMigrationCling() {
+        mLauncher.onLauncherClingShown();
         mLauncher.hideWorkspaceSearchAndHotseat();
 
         ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
@@ -136,7 +150,9 @@
         final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
         mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
                 : R.layout.longpress_cling_content, content);
-        content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
+        final View button = content.findViewById(R.id.cling_dismiss_longpress_info);
+        button.setOnClickListener(this);
+        button.setOnKeyListener(this);
 
         if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
             Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
@@ -144,6 +160,7 @@
             content.setBackground(bg);
         }
 
+        mLauncher.onLauncherClingShown();
         root.addView(cling);
 
         if (showWelcome) {
@@ -180,7 +197,12 @@
     @Thunk void dismissLongPressCling() {
         Runnable dismissCb = new Runnable() {
             public void run() {
-                dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
+                Runnable cb = new Runnable() {
+                    public void run() {
+                        mLauncher.onLauncherClingDismissed();
+                    }
+                };
+                dismissCling(mLauncher.findViewById(R.id.longpress_cling), cb,
                         WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
             }
         };
@@ -215,10 +237,6 @@
     /** Returns whether the clings are enabled or should be shown */
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     private boolean areClingsEnabled() {
-        if (DISABLE_CLINGS) {
-            return false;
-        }
-
         // disable clings when running in a test harness
         if(ActivityManager.isRunningInTestHarness()) return false;
 
@@ -231,10 +249,7 @@
 
         // Restricted secondary users (child mode) will potentially have very few apps
         // seeded when they start up for the first time. Clings won't work well with that
-        boolean supportsLimitedUsers =
-                android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
-        Account[] accounts = AccountManager.get(mLauncher).getAccounts();
-        if (supportsLimitedUsers && accounts.length == 0) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
             Bundle restrictions = um.getUserRestrictions();
             if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 0311d01..43f92c4 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1284,6 +1284,7 @@
             }
         } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
                 || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+            UserManagerCompat.getInstance(context).enableAndResetCache();
             forceReload();
         }
     }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index e49cd1c..b2ab618 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -80,13 +80,6 @@
 public class LauncherStateTransitionAnimation {
 
     /**
-     * Callbacks made during the state transition
-     */
-    interface Callbacks {
-        public void onStateTransitionHideSearchBar();
-    }
-
-    /**
      * Private callbacks made during transition setup.
      */
     static abstract class PrivateTransitionCallbacks {
@@ -111,12 +104,10 @@
     public static final int SINGLE_FRAME_DELAY = 16;
 
     @Thunk Launcher mLauncher;
-    @Thunk Callbacks mCb;
-    @Thunk AnimatorSet mStateAnimation;
+    @Thunk AnimatorSet mCurrentAnimation;
 
-    public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
+    public LauncherStateTransitionAnimation(Launcher l) {
         mLauncher = l;
-        mCb = cb;
     }
 
     /**
@@ -125,8 +116,8 @@
      * @param startSearchAfterTransition Immediately starts app search after the transition to
      *                                   All Apps is completed.
      */
-    public void startAnimationToAllApps(final boolean animated,
-            final boolean startSearchAfterTransition) {
+    public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
+            final boolean animated, final boolean startSearchAfterTransition) {
         final AllAppsContainerView toView = mLauncher.getAppsView();
         final View buttonView = mLauncher.getAllAppsButton();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@@ -159,15 +150,16 @@
             }
         };
         // Only animate the search bar if animating from spring loaded mode back to all apps
-        startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, buttonView, toView,
-                toView.getContentView(), toView.getRevealView(), toView.getSearchBarView(),
-                animated, true /* hideSearchBar */, cb);
+        mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
+                Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
+                toView.getRevealView(), toView.getSearchBarView(), animated, cb);
     }
 
     /**
      * Starts an animation to the widgets view.
      */
-    public void startAnimationToWidgets(final boolean animated) {
+    public void startAnimationToWidgets(final Workspace.State fromWorkspaceState,
+            final boolean animated) {
         final WidgetsContainerView toView = mLauncher.getWidgetsView();
         final View buttonView = mLauncher.getWidgetsButton();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@@ -176,17 +168,17 @@
                 return 0.3f;
             }
         };
-        startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, buttonView, toView,
-                toView.getContentView(), toView.getRevealView(), null, animated,
-                true /* hideSearchBar */, cb);
+        mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
+                Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
+                toView.getRevealView(), null, animated, cb);
     }
 
     /**
      * Starts and animation to the workspace from the current overlay view.
      */
     public void startAnimationToWorkspace(final Launcher.State fromState,
-              final Workspace.State toWorkspaceState,
-              final boolean animated, final Runnable onCompleteRunnable) {
+            final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+            final boolean animated, final Runnable onCompleteRunnable) {
         if (toWorkspaceState != Workspace.State.NORMAL &&
                 toWorkspaceState != Workspace.State.SPRING_LOADED &&
                 toWorkspaceState != Workspace.State.OVERVIEW) {
@@ -194,9 +186,11 @@
         }
 
         if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
-            startAnimationToWorkspaceFromAllApps(toWorkspaceState, animated, onCompleteRunnable);
+            startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
+                    animated, onCompleteRunnable);
         } else {
-            startAnimationToWorkspaceFromWidgets(toWorkspaceState, animated, onCompleteRunnable);
+            startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
+                    animated, onCompleteRunnable);
         }
     }
 
@@ -204,10 +198,11 @@
      * Creates and starts a new animation to a particular overlay view.
      */
     @SuppressLint("NewApi")
-    private void startAnimationToOverlay(final Workspace.State toWorkspaceState,
-            final View buttonView, final View toView, final View contentView, final View revealView,
-            final View overlaySearchBarView, final boolean animated, final boolean hideSearchBar,
-            final PrivateTransitionCallbacks pCb) {
+    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,
+            final boolean animated, final PrivateTransitionCallbacks pCb) {
+        final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.isLmpOrAbove();
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
@@ -227,11 +222,13 @@
         // Create the workspace animation.
         // NOTE: this call apparently also sets the state for the workspace if !animated
         Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
-                animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
+                animated, layerViews);
+
+        // Animate the search bar
+        startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
+                animated ? revealDuration : 0, overlaySearchBarView);
 
         if (animated && initialized) {
-            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
-
             // Setup the reveal view animation
             int width = revealView.getMeasuredWidth();
             int height = revealView.getMeasuredHeight();
@@ -271,7 +268,7 @@
 
             // Play the animation
             layerViews.put(revealView, BUILD_AND_SET_LAYER);
-            mStateAnimation.play(panelAlphaAndDrift);
+            animation.play(panelAlphaAndDrift);
 
             if (overlaySearchBarView != null) {
                 overlaySearchBarView.setAlpha(0f);
@@ -279,7 +276,7 @@
                 searchBarAlpha.setDuration(100);
                 searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
                 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
-                mStateAnimation.play(searchBarAlpha);
+                animation.play(searchBarAlpha);
             }
 
             // Setup the animation for the content view
@@ -294,13 +291,13 @@
             pageDrift.setDuration(revealDuration);
             pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
             pageDrift.setStartDelay(itemsAlphaStagger);
-            mStateAnimation.play(pageDrift);
+            animation.play(pageDrift);
 
             ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
             itemsAlpha.setDuration(revealDuration);
             itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
             itemsAlpha.setStartDelay(itemsAlphaStagger);
-            mStateAnimation.play(itemsAlpha);
+            animation.play(itemsAlpha);
 
             if (material) {
                 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
@@ -313,10 +310,10 @@
                 if (listener != null) {
                     reveal.addListener(listener);
                 }
-                mStateAnimation.play(reveal);
+                animation.play(reveal);
             }
 
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+            animation.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
@@ -332,12 +329,8 @@
                         }
                     }
 
-                    if (hideSearchBar) {
-                        mCb.onStateTransitionHideSearchBar();
-                    }
-
                     // This can hold unnecessary references to views.
-                    mStateAnimation = null;
+                    cleanupAnimation();
                     pCb.onTransitionComplete();
                 }
 
@@ -345,7 +338,7 @@
 
             // Play the workspace animation
             if (workspaceAnim != null) {
-                mStateAnimation.play(workspaceAnim);
+                animation.play(workspaceAnim);
             }
 
             // Dispatch the prepare transition signal
@@ -353,12 +346,12 @@
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
 
 
-            final AnimatorSet stateAnimation = mStateAnimation;
+            final AnimatorSet stateAnimation = animation;
             final Runnable startAnimRunnable = new Runnable() {
                 public void run() {
-                    // Check that mStateAnimation hasn't changed while
+                    // Check that mCurrentAnimation hasn't changed while
                     // we waited for a layout/draw pass
-                    if (mStateAnimation != stateAnimation)
+                    if (mCurrentAnimation != stateAnimation)
                         return;
                     dispatchOnLauncherTransitionStart(fromView, animated, false);
                     dispatchOnLauncherTransitionStart(toView, animated, false);
@@ -377,12 +370,14 @@
                     // Focus the new view
                     toView.requestFocus();
 
-                    mStateAnimation.start();
+                    stateAnimation.start();
                 }
             };
             toView.bringToFront();
             toView.setVisibility(View.VISIBLE);
             toView.post(startAnimRunnable);
+
+            return animation;
         } else {
             toView.setTranslationX(0.0f);
             toView.setTranslationY(0.0f);
@@ -394,10 +389,6 @@
             // Show the content view
             contentView.setVisibility(View.VISIBLE);
 
-            if (hideSearchBar) {
-                mCb.onStateTransitionHideSearchBar();
-            }
-
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
             dispatchOnLauncherTransitionStart(fromView, animated, false);
             dispatchOnLauncherTransitionEnd(fromView, animated, false);
@@ -405,18 +396,19 @@
             dispatchOnLauncherTransitionStart(toView, animated, false);
             dispatchOnLauncherTransitionEnd(toView, animated, false);
             pCb.onTransitionComplete();
+
+            return null;
         }
     }
 
     /**
      * Starts and animation to the workspace from the apps view.
      */
-    private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
-            final boolean animated, final Runnable onCompleteRunnable) {
+    private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
+            final Workspace.State toWorkspaceState, final boolean animated, 
+            final Runnable onCompleteRunnable) {
         AllAppsContainerView appsView = mLauncher.getAppsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
-            int[] mAllAppsToPanelDelta;
-
             @Override
             float getMaterialRevealViewFinalAlpha(View revealView) {
                 // No alpha anim from all apps
@@ -448,16 +440,18 @@
             }
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
-        startAnimationToWorkspaceFromOverlay(toWorkspaceState, mLauncher.getAllAppsButton(),
-                appsView, appsView.getContentView(), appsView.getRevealView(),
-                appsView.getSearchBarView(), animated, onCompleteRunnable, cb);
+        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
+                mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
+                appsView.getRevealView(), appsView.getSearchBarView(), animated,
+                onCompleteRunnable, cb);
     }
 
     /**
      * Starts and animation to the workspace from the widgets view.
      */
-    private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
-              final boolean animated, final Runnable onCompleteRunnable) {
+    private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
+            final Workspace.State toWorkspaceState, final boolean animated, 
+            final Runnable onCompleteRunnable) {
         final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             @Override
@@ -475,18 +469,21 @@
                 };
             }
         };
-        startAnimationToWorkspaceFromOverlay(toWorkspaceState, mLauncher.getWidgetsButton(),
-                widgetsView, widgetsView.getContentView(), widgetsView.getRevealView(), null,
-                animated, onCompleteRunnable, cb);
-        }
+        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
+                toWorkspaceState, mLauncher.getWidgetsButton(), widgetsView,
+                widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
+                onCompleteRunnable, cb);
+    }
 
     /**
      * Creates and starts a new animation to the workspace.
      */
-    private void startAnimationToWorkspaceFromOverlay(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, final PrivateTransitionCallbacks pCb) {
+    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,
+            final PrivateTransitionCallbacks pCb) {
+        final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.isLmpOrAbove();
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
@@ -506,15 +503,16 @@
         // Create the workspace animation.
         // NOTE: this call apparently also sets the state for the workspace if !animated
         Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
-                animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
-                layerViews);
+                animated, layerViews);
+
+        // Animate the search bar
+        startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
+                animated ? revealDuration : 0, overlaySearchBarView);
 
         if (animated && initialized) {
-            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
-
             // Play the workspace animation
             if (workspaceAnim != null) {
-                mStateAnimation.play(workspaceAnim);
+                animation.play(workspaceAnim);
             }
 
             // hideAppsCustomizeHelper is called in some cases when it is already hidden
@@ -553,14 +551,14 @@
                 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
                 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
                 panelDriftY.setInterpolator(decelerateInterpolator);
-                mStateAnimation.play(panelDriftY);
+                animation.play(panelDriftY);
 
                 ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
                         0, revealViewToXDrift);
                 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
                 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
                 panelDriftX.setInterpolator(decelerateInterpolator);
-                mStateAnimation.play(panelDriftX);
+                animation.play(panelDriftX);
 
                 // Setup animation for the reveal panel alpha
                 final float revealViewToAlpha = !material ? 0f :
@@ -571,7 +569,7 @@
                     panelAlpha.setDuration(material ? revealDuration : 150);
                     panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
                     panelAlpha.setInterpolator(decelerateInterpolator);
-                    mStateAnimation.play(panelAlpha);
+                    animation.play(panelAlpha);
                 }
 
                 // Setup the animation for the content view
@@ -584,13 +582,13 @@
                 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
                 pageDrift.setInterpolator(decelerateInterpolator);
                 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
-                mStateAnimation.play(pageDrift);
+                animation.play(pageDrift);
 
                 contentView.setAlpha(1f);
                 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
                 itemsAlpha.setDuration(100);
                 itemsAlpha.setInterpolator(decelerateInterpolator);
-                mStateAnimation.play(itemsAlpha);
+                animation.play(itemsAlpha);
 
                 if (overlaySearchBarView != null) {
                     overlaySearchBarView.setAlpha(1f);
@@ -599,7 +597,7 @@
                     searchAlpha.setInterpolator(decelerateInterpolator);
                     searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
                     layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
-                    mStateAnimation.play(searchAlpha);
+                    animation.play(searchAlpha);
                 }
 
                 if (material) {
@@ -615,14 +613,14 @@
                     if (listener != null) {
                         reveal.addListener(listener);
                     }
-                    mStateAnimation.play(reveal);
+                    animation.play(reveal);
                 }
 
                 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
                 dispatchOnLauncherTransitionPrepare(toView, animated, true);
             }
 
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+            animation.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     fromView.setVisibility(View.GONE);
@@ -652,18 +650,19 @@
                     }
 
                     // This can hold unnecessary references to views.
-                    mStateAnimation = null;
+                    cleanupAnimation();
                     pCb.onTransitionComplete();
                 }
             });
 
-            final AnimatorSet stateAnimation = mStateAnimation;
+            final AnimatorSet stateAnimation = animation;
             final Runnable startAnimRunnable = new Runnable() {
                 public void run() {
-                    // Check that mStateAnimation hasn't changed while
+                    // Check that mCurrentAnimation hasn't changed while
                     // we waited for a layout/draw pass
-                    if (mStateAnimation != stateAnimation)
+                    if (mCurrentAnimation != stateAnimation)
                         return;
+
                     dispatchOnLauncherTransitionStart(fromView, animated, false);
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
@@ -677,10 +676,12 @@
                             v.buildLayer();
                         }
                     }
-                    mStateAnimation.start();
+                    stateAnimation.start();
                 }
             };
             fromView.post(startAnimRunnable);
+
+            return animation;
         } else {
             fromView.setVisibility(View.GONE);
             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
@@ -695,9 +696,44 @@
             if (onCompleteRunnable != null) {
                 onCompleteRunnable.run();
             }
+
+            return null;
         }
     }
 
+    /**
+     * 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 ((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);
+            } 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);
+                    }
+                });
+            } else {
+                // Otherwise, then just animate the workspace search bar normally
+                mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
+            }
+        } else {
+            // If there is no overlay search bar, then just animate the workspace search bar
+            mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
+        }
+    }
 
     /**
      * Dispatches the prepare-transition event to suitable views.
@@ -748,10 +784,14 @@
      * Cancels the current animation.
      */
     private void cancelAnimation() {
-        if (mStateAnimation != null) {
-            mStateAnimation.setDuration(0);
-            mStateAnimation.cancel();
-            mStateAnimation = null;
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.setDuration(0);
+            mCurrentAnimation.cancel();
+            mCurrentAnimation = null;
         }
     }
+
+    @Thunk void cleanupAnimation() {
+        mCurrentAnimation = null;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherViewPropertyAnimator.java b/src/com/android/launcher3/LauncherViewPropertyAnimator.java
index 4cafbbf..4406a2c 100644
--- a/src/com/android/launcher3/LauncherViewPropertyAnimator.java
+++ b/src/com/android/launcher3/LauncherViewPropertyAnimator.java
@@ -26,6 +26,7 @@
 import java.util.EnumSet;
 
 public class LauncherViewPropertyAnimator extends Animator implements AnimatorListener {
+
     enum Properties {
             TRANSLATION_X,
             TRANSLATION_Y,
@@ -51,13 +52,12 @@
     long mStartDelay;
     long mDuration;
     TimeInterpolator mInterpolator;
-    ArrayList<Animator.AnimatorListener> mListeners;
+    ArrayList<Animator.AnimatorListener> mListeners = new ArrayList<>();
     boolean mRunning = false;
     FirstFrameAnimatorHelper mFirstFrameHelper;
 
     public LauncherViewPropertyAnimator(View target) {
         mTarget = target;
-        mListeners = new ArrayList<Animator.AnimatorListener>();
     }
 
     @Override
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 824acad..f54d9d8 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -18,32 +18,57 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.util.Thunk;
+
 /*
  * Ths bar will manage the transition between the QSB search bar and the delete drop
  * targets so that each of the individual IconDropTargets don't have to.
  */
 public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
 
-    private static final int TRANSITION_DURATION = 200;
+    /** 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);
 
-    private ObjectAnimator mShowDropTargetBarAnim;
-    private ValueAnimator mHideSearchBarAnim;
+        private final float mSearchBarAlpha;
+        private final float mDropTargetBarAlpha;
+
+        State(float sbAlpha, float dtbAlpha) {
+            mSearchBarAlpha = sbAlpha;
+            mDropTargetBarAlpha = dtbAlpha;
+        }
+
+        float getSearchBarAlpha() {
+            return mSearchBarAlpha;
+        }
+
+        float getDropTargetBarAlpha() {
+            return mDropTargetBarAlpha;
+        }
+    }
+
+    private static int DEFAULT_DRAG_FADE_DURATION = 175;
+
+    private LauncherViewPropertyAnimator mDropTargetBarAnimator;
+    private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
     private static final AccelerateInterpolator sAccelerateInterpolator =
             new AccelerateInterpolator();
 
-    private boolean mIsSearchBarHidden;
-    private View mQSBSearchBar;
-    private View mDropTargetBar;
+    private State mState = State.SEARCH_BAR;
+    @Thunk View mQSB;
+    @Thunk View mDropTargetBar;
     private boolean mDeferOnDragEnd = false;
+    @Thunk boolean mAccessibilityEnabled = false;
 
     // Drop targets
     private ButtonDropTarget mInfoDropTarget;
@@ -75,39 +100,6 @@
         mUninstallDropTarget.setLauncher(launcher);
     }
 
-    public void setQsbSearchBar(View qsb) {
-        mQSBSearchBar = qsb;
-        if (mQSBSearchBar != null) {
-            mHideSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, ALPHA, 1f, 0f);
-            setupAnimation(mHideSearchBarAnim, mQSBSearchBar);
-        } else {
-            // Create a no-op animation of the search bar is null
-            mHideSearchBarAnim = ValueAnimator.ofFloat(0, 0);
-            mHideSearchBarAnim.setDuration(TRANSITION_DURATION);
-        }
-    }
-
-    private void prepareStartAnimation(View v) {
-        // Enable the hw layers before the animation starts (will be disabled in the onAnimationEnd
-        // callback below)
-        if (v != null) {
-            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        }
-    }
-
-    private void setupAnimation(ValueAnimator anim, final View v) {
-        anim.setInterpolator(sAccelerateInterpolator);
-        anim.setDuration(TRANSITION_DURATION);
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (v != null) {
-                    v.setLayerType(View.LAYER_TYPE_NONE, null);
-                }
-            }
-        });
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -124,72 +116,84 @@
 
         // Create the various fade animations
         mDropTargetBar.setAlpha(0f);
-        mShowDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, ALPHA, 0f, 1f);
-        setupAnimation(mShowDropTargetBarAnim, mDropTargetBar);
+        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);
+                }
+            }
+        });
     }
 
-    /**
-     * Finishes all the animations on the search and drop target bars.
-     */
-    public void finishAnimations() {
-        prepareStartAnimation(mDropTargetBar);
-        mShowDropTargetBarAnim.reverse();
-        prepareStartAnimation(mQSBSearchBar);
-        mHideSearchBarAnim.reverse();
-    }
+    public void setQsbSearchBar(View qsb) {
+        mQSB = qsb;
+        if (mQSB != null) {
+            // Update the search ber animation
+            mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB);
+            mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator);
+            mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    // Ensure that the view is visible for the animation
+                    if (mQSB != null) {
+                        mQSB.setVisibility(View.VISIBLE);
+                    }
+                }
 
-    /**
-     * Shows the search bar.
-     */
-    public void showSearchBar(boolean animated) {
-        if (!mIsSearchBarHidden) return;
-        if (animated) {
-            prepareStartAnimation(mQSBSearchBar);
-            mHideSearchBarAnim.reverse();
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (mQSB != null) {
+                        AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
+                    }
+                }
+            });
         } else {
-            mHideSearchBarAnim.cancel();
-            if (mQSBSearchBar != null) {
-                mQSBSearchBar.setAlpha(1f);
+            mQSBSearchBarAnimator = null;
+        }
+    }
+
+    /**
+     * Animates the current search bar state to a new state.  If the {@param duration} is 0, then
+     * the state is applied immediately.
+     */
+    public void animateToState(State newState, int duration) {
+        if (mState != newState) {
+            mState = newState;
+
+            // Update the accessibility state
+            AccessibilityManager am = (AccessibilityManager)
+                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+            mAccessibilityEnabled = am.isEnabled();
+
+            animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
+                    duration);
+            animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
+                    duration);
+        }
+    }
+
+    /**
+     * Convenience method to animate the alpha of a view using hardware layers.
+     */
+    private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
+            int duration) {
+        if (v != null && Float.compare(v.getAlpha(), alpha) != 0) {
+            if (duration > 0) {
+                animator.alpha(alpha).withLayer().setDuration(duration).start();
+            } else {
+                v.setAlpha(alpha);
+                AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
             }
         }
-        mIsSearchBarHidden = false;
-    }
-
-    /**
-     * Hides the search bar.  We only use this for clings.
-     */
-    public void hideSearchBar(boolean animated) {
-        if (mIsSearchBarHidden) return;
-        if (animated) {
-            prepareStartAnimation(mQSBSearchBar);
-            mHideSearchBarAnim.start();
-        } else {
-            mHideSearchBarAnim.cancel();
-            if (mQSBSearchBar != null) {
-                mQSBSearchBar.setAlpha(0f);
-            }
-        }
-        mIsSearchBarHidden = true;
-    }
-
-    /**
-     * Shows the drop target bar.
-     */
-    public void showDeleteTarget() {
-        // Animate out the QSB search bar, and animate in the drop target bar
-        prepareStartAnimation(mDropTargetBar);
-        mShowDropTargetBarAnim.start();
-        hideSearchBar(true);
-    }
-
-    /**
-     * Hides the drop target bar.
-     */
-    public void hideDeleteTarget() {
-        // Restore the QSB search bar, and animate out the drop target bar
-        prepareStartAnimation(mDropTargetBar);
-        mShowDropTargetBarAnim.reverse();
-        showSearchBar(true);
     }
 
     /*
@@ -197,9 +201,13 @@
      */
     @Override
     public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
-        showDeleteTarget();
+        animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
     }
 
+    /**
+     * This is called to defer hiding the delete drop target until the drop animation has completed,
+     * instead of hiding immediately when the drag has ended.
+     */
     public void deferOnDragEnd() {
         mDeferOnDragEnd = true;
     }
@@ -207,22 +215,25 @@
     @Override
     public void onDragEnd() {
         if (!mDeferOnDragEnd) {
-            hideDeleteTarget();
+            animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
         } else {
             mDeferOnDragEnd = false;
         }
     }
 
+    /**
+     * @return the bounds of the QSB search bar.
+     */
     public Rect getSearchBarBounds() {
-        if (mQSBSearchBar != null) {
+        if (mQSB != null) {
             final int[] pos = new int[2];
-            mQSBSearchBar.getLocationOnScreen(pos);
+            mQSB.getLocationOnScreen(pos);
 
             final Rect rect = new Rect();
             rect.left = pos[0];
             rect.top = pos[1];
-            rect.right = pos[0] + mQSBSearchBar.getWidth();
-            rect.bottom = pos[1] + mQSBSearchBar.getHeight();
+            rect.right = pos[0] + mQSB.getWidth();
+            rect.bottom = pos[1] + mQSB.getHeight();
             return rect;
         } else {
             return null;
@@ -230,8 +241,8 @@
     }
 
     public void enableAccessibleDrag(boolean enable) {
-        if (mQSBSearchBar != null) {
-            mQSBSearchBar.setVisibility(enable ? View.GONE : View.VISIBLE);
+        if (mQSB != null) {
+            mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
         }
         mInfoDropTarget.enableAccessibleDrag(enable);
         mDeleteDropTarget.enableAccessibleDrag(enable);
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 1fb795a..3d50587 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -212,7 +212,6 @@
     public void removeObsoletePreviews(ArrayList<Object> list) {
         Utilities.assertWorkerThread();
 
-        LongSparseArray<UserHandleCompat> userIdCache = new LongSparseArray<>();
         LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
 
         for (Object obj : list) {
@@ -227,15 +226,7 @@
                 pkg = info.provider.getPackageName();
             }
 
-            int userIdIndex = userIdCache.indexOfValue(user);
-            final long userId;
-            if (userIdIndex < 0) {
-                userId = mUserManager.getSerialNumberForUser(user);
-                userIdCache.put(userId, user);
-            } else {
-                userId = userIdCache.keyAt(userIdIndex);
-            }
-
+            final long userId = mUserManager.getSerialNumberForUser(user);
             HashSet<String> packages = validPackages.get(userId);
             if (packages == null) {
                 packages = new HashSet<>();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index cb844bb..1fbc779 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -178,7 +178,24 @@
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
 
-    enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
+    enum State {
+        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR),
+        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE),
+        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET),
+        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE),
+        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
+
+        private final SearchDropTargetBar.State mBarState;
+
+        State(SearchDropTargetBar.State searchBarState) {
+            mBarState = searchBarState;
+        }
+
+        public SearchDropTargetBar.State getSearchDropTargetBarState() {
+            return mBarState;
+        }
+    };
+
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
@@ -1558,7 +1575,7 @@
             // Reset our click listener
             setOnClickListener(mLauncher);
         }
-        mLauncher.getSearchBar().enableAccessibleDrag(enable);
+        mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
         mLauncher.getHotseat().getLayout()
             .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
     }
@@ -1976,10 +1993,10 @@
      * to that new state.
      */
     public Animator setStateWithAnimation(State toState, boolean animated,
-            boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
+            HashMap<View, Integer> layerViews) {
         // Create the animation to the new state
         Animator workspaceAnim =  mStateTransitionAnimation.getAnimationToState(mState,
-                toState, animated, hasOverlaySearchBar, layerViews);
+                toState, animated, layerViews);
 
         // Update the current state
         mState = toState;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 652b194..f097585 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -221,14 +221,13 @@
     }
 
     public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
-            boolean animated, boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
+            boolean animated, HashMap<View, Integer> layerViews) {
         AccessibilityManager am = (AccessibilityManager)
                 mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
         final boolean accessibilityEnabled = am.isEnabled();
         TransitionStates states = new TransitionStates(fromState, toState);
-        int duration = getAnimationDuration(states);
-        animateWorkspace(states, animated, duration, layerViews, accessibilityEnabled);
-        animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews,
+        int workspaceDuration = getAnimationDuration(states);
+        animateWorkspace(states, animated, workspaceDuration, layerViews,
                 accessibilityEnabled);
         animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
         return mStateAnimator;
@@ -470,75 +469,9 @@
     }
 
     /**
-     * Coordinates with the workspace animation to animate the search bar.
-     *
-     * TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the
-     *       bar has no idea that it is hidden, and this has no idea what state the bar is
-     *       actually in.
-     */
-    private void animateSearchBar(TransitionStates states, boolean animated, int duration,
-            boolean hasOverlaySearchBar, final HashMap<View, Integer> layerViews,
-            final boolean accessibilityEnabled) {
-
-        // The search bar is only visible in the workspace
-        final View searchBar = mLauncher.getOrCreateQsbBar();
-        if (searchBar != null) {
-            final boolean searchBarWillBeShown = states.stateIsNormal;
-            final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f;
-            if (animated) {
-                if (hasOverlaySearchBar) {
-                    // If there is an overlay search bar, then we will coordinate with it.
-                    mStateAnimator.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationStart(Animator animation) {
-                            // If we are transitioning to a visible search bar, show it immediately
-                            // and let the overlay search bar has faded out
-                            if (searchBarWillBeShown) {
-                                searchBar.setAlpha(finalSearchBarAlpha);
-                                AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
-                            }
-                        }
-
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            // If we are transitioning to a hidden search bar, hide it only after
-                            // the overlay search bar has faded in
-                            if (!searchBarWillBeShown) {
-                                searchBar.setAlpha(finalSearchBarAlpha);
-                                AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
-                            }
-                        }
-                    });
-                } else {
-                    // Otherwise, we can just do the normal animation
-                    LauncherViewPropertyAnimator searchBarAlpha =
-                            new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha);
-                    searchBarAlpha.addListener(new AlphaUpdateListener(searchBar,
-                            accessibilityEnabled));
-                    searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                    if (layerViews != null) {
-                        // If layerViews is not null, we add these views, and indicate that
-                        // the caller can manage layer state.
-                        layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-                    } else {
-                        // Otherwise let the animator handle layer management.
-                        searchBarAlpha.withLayer();
-                    }
-                    searchBarAlpha.setDuration(duration);
-                    mStateAnimator.play(searchBarAlpha);
-                }
-            } else {
-                // Set the search bar state immediately
-                searchBar.setAlpha(finalSearchBarAlpha);
-                AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
-            }
-        }
-    }
-
-    /**
      * Animates the background scrim. Add to the state animator to prevent jankiness.
      *
-     * @param finalAlpha the final alpha for the background scrim
+     * @param states the current and final workspace states
      * @param animated whether or not to set the background alpha immediately
      * @duration duration of the animation
      */
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index a79d946..2ff1e7b 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -28,16 +28,29 @@
     protected UserManagerCompat() {
     }
 
+    private static final Object sInstanceLock = new Object();
+    private static UserManagerCompat sInstance;
+
     public static UserManagerCompat getInstance(Context context) {
-        if (Utilities.isLmpOrAbove()) {
-            return new UserManagerCompatVL(context);
-        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            return new UserManagerCompatV17(context);
-        } else {
-            return new UserManagerCompatV16();
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                if (Utilities.isLmpOrAbove()) {
+                    sInstance = new UserManagerCompatVL(context.getApplicationContext());
+                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                    sInstance = new UserManagerCompatV17(context.getApplicationContext());
+                } else {
+                    sInstance = new UserManagerCompatV16();
+                }
+            }
+            return sInstance;
         }
     }
 
+    /**
+     * Creates a cache for users.
+     */
+    public abstract void enableAndResetCache();
+
     public abstract List<UserHandleCompat> getUserProfiles();
     public abstract long getSerialNumberForUser(UserHandleCompat user);
     public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index ffe698c..85aee57 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -53,4 +53,8 @@
     public long getUserCreationTime(UserHandleCompat user) {
         return 0;
     }
+
+    @Override
+    public void enableAndResetCache() {
+    }
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java
index c42c00c..75203b7 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV17.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java
@@ -21,8 +21,18 @@
 import android.os.Build;
 import android.os.UserManager;
 
+import com.android.launcher3.util.LongArrayMap;
+
+import java.util.HashMap;
+
 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
 public class UserManagerCompatV17 extends UserManagerCompatV16 {
+
+    protected LongArrayMap<UserHandleCompat> mUsers;
+    // Create a separate reverse map as LongArrayMap.indexOfValue checks if objects are same
+    // and not {@link Object#equals}
+    protected HashMap<UserHandleCompat, Long> mUserToSerialMap;
+
     protected UserManager mUserManager;
 
     UserManagerCompatV17(Context context) {
@@ -30,11 +40,34 @@
     }
 
     public long getSerialNumberForUser(UserHandleCompat user) {
+        synchronized (this) {
+            if (mUserToSerialMap != null) {
+                Long serial = mUserToSerialMap.get(user);
+                return serial == null ? 0 : serial;
+            }
+        }
         return mUserManager.getSerialNumberForUser(user.getUser());
     }
 
     public UserHandleCompat getUserForSerialNumber(long serialNumber) {
+        synchronized (this) {
+            if (mUsers != null) {
+                return mUsers.get(serialNumber);
+            }
+        }
         return UserHandleCompat.fromUser(mUserManager.getUserForSerialNumber(serialNumber));
     }
+
+    @Override
+    public void enableAndResetCache() {
+        synchronized (this) {
+            mUsers = new LongArrayMap<>();
+            mUserToSerialMap = new HashMap<>();
+            UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+            long serial = mUserManager.getSerialNumberForUser(myUser.getUser());
+            mUsers.put(serial, myUser);
+            mUserToSerialMap.put(myUser, serial);
+        }
+    }
 }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index dd7a726..4d404db 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -24,9 +24,13 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.UserHandle;
+
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.util.LongArrayMap;
+
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -43,7 +47,32 @@
     }
 
     @Override
+    public void enableAndResetCache() {
+        synchronized (this) {
+            mUsers = new LongArrayMap<>();
+            mUserToSerialMap = new HashMap<>();
+            List<UserHandle> users = mUserManager.getUserProfiles();
+            if (users != null) {
+                for (UserHandle user : users) {
+                    long serial = mUserManager.getSerialNumberForUser(user);
+                    UserHandleCompat userCompat = UserHandleCompat.fromUser(user);
+                    mUsers.put(serial, userCompat);
+                    mUserToSerialMap.put(userCompat, serial);
+                }
+            }
+        }
+    }
+
+    @Override
     public List<UserHandleCompat> getUserProfiles() {
+        synchronized (this) {
+            if (mUsers != null) {
+                List<UserHandleCompat> users = new ArrayList<>();
+                users.addAll(mUserToSerialMap.keySet());
+                return users;
+            }
+        }
+
         List<UserHandle> users = mUserManager.getUserProfiles();
         if (users == null) {
             return Collections.emptyList();
diff --git a/src/com/android/launcher3/model/AbstractUserComparator.java b/src/com/android/launcher3/model/AbstractUserComparator.java
index cf47ce6..bd28560 100644
--- a/src/com/android/launcher3/model/AbstractUserComparator.java
+++ b/src/com/android/launcher3/model/AbstractUserComparator.java
@@ -22,14 +22,12 @@
 import com.android.launcher3.compat.UserManagerCompat;
 
 import java.util.Comparator;
-import java.util.HashMap;
 
 /**
  * A comparator to arrange items based on user profiles.
  */
 public abstract class AbstractUserComparator<T extends ItemInfo> implements Comparator<T> {
 
-    private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
     private final UserManagerCompat mUserManager;
     private final UserHandleCompat mMyUser;
 
@@ -43,25 +41,9 @@
         if (mMyUser.equals(lhs.user)) {
             return -1;
         } else {
-            Long aUserSerial = getAndCacheUserSerial(lhs.user);
-            Long bUserSerial = getAndCacheUserSerial(rhs.user);
+            Long aUserSerial = mUserManager.getSerialNumberForUser(lhs.user);
+            Long bUserSerial = mUserManager.getSerialNumberForUser(rhs.user);
             return aUserSerial.compareTo(bUserSerial);
         }
     }
-
-    /**
-     * Returns the user serial for this user, using a cached serial if possible.
-     */
-    private Long getAndCacheUserSerial(UserHandleCompat user) {
-        Long userSerial = mUserSerialCache.get(user);
-        if (userSerial == null) {
-            userSerial = mUserManager.getSerialNumberForUser(user);
-            mUserSerialCache.put(user, userSerial);
-        }
-        return userSerial;
-    }
-
-    public void clearUserCache() {
-        mUserSerialCache.clear();
-    }
 }
diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java
index c4b74d4..5f80037 100644
--- a/src/com/android/launcher3/model/AppNameComparator.java
+++ b/src/com/android/launcher3/model/AppNameComparator.java
@@ -68,8 +68,6 @@
      * Returns a locale-aware comparator that will alphabetically order a list of applications.
      */
     public Comparator<ItemInfo> getAppInfoComparator() {
-        // Clear the user serial cache so that we get serials as needed in the comparator
-        mAppInfoComparator.clearUserCache();
         return mAppInfoComparator;
     }