Moving the QSB to the workspace grid.
am: da4fe1a624

* commit 'da4fe1a6244457f144e0a331cada3ada17157809':
  Moving the QSB to the workspace grid.

Change-Id: Ibfd0109be932430047c1dc177f62e71d42dd3d1f
diff --git a/res/layout/qsb_container.xml b/res/layout/qsb_container.xml
new file mode 100644
index 0000000..3de2876
--- /dev/null
+++ b/res/layout/qsb_container.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.QsbContainerView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/qsb_container"
+        android:padding="0dp" />
\ No newline at end of file
diff --git a/res/layout/qsb_default_view.xml b/res/layout/qsb_default_view.xml
new file mode 100644
index 0000000..82bdea5
--- /dev/null
+++ b/res/layout/qsb_default_view.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_margin="16dp"
+        android:layout_gravity="center_vertical"
+        android:background="@drawable/quantum_panel_shape"
+        android:elevation="2dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:layout_width="0dp"
+            android:id="@+id/btn_qsb_search"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center_vertical"
+            android:paddingStart="16dp"
+            android:text="@string/abandoned_search"
+            android:textColor="@color/quantum_panel_text_color"
+            android:textAppearance="?android:textAppearanceMedium"
+            android:clickable="true"
+            android:background="?android:attr/selectableItemBackground" />
+        <ImageView
+            android:layout_width="48dp"
+            android:id="@+id/btn_qsb_setup"
+            android:clickable="true"
+            android:visibility="gone"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_setting"
+            android:tint="@color/quantum_panel_text_color"
+            android:contentDescription="@string/gadget_setup_text"
+            android:padding="8dp"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index e3bb5fa..cfaa6a3 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2708,7 +2708,7 @@
 
         /**
          * Indicates whether this item can be reordered. Always true except in the case of the
-         * the AllApps button.
+         * the AllApps button and QSB place holder.
          */
         public boolean canReorder = true;
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 13e451a..6a95a90 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -45,20 +45,16 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
@@ -113,7 +109,6 @@
 import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.pageindicators.PageIndicatorLine;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
@@ -183,6 +178,9 @@
     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
 
+    public static final String ACTION_APPWIDGET_HOST_RESET =
+            "com.android.launcher3.intent.ACTION_APPWIDGET_HOST_RESET";
+
     // Type: int
     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
     // Type: int
@@ -197,9 +195,6 @@
     static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
     static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
 
-    private static final String QSB_WIDGET_ID = "qsb_widget_id";
-    private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider";
-
     /** The different states that Launcher can be in. */
     enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
         WIDGETS, WIDGETS_SPRING_LOADED }
@@ -219,8 +214,19 @@
     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
 
-    private final BroadcastReceiver mCloseSystemDialogsReceiver
-            = new CloseSystemDialogsIntentReceiver();
+    private final BroadcastReceiver mUiBroadcastReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                closeSystemDialogs();
+            } else if (ACTION_APPWIDGET_HOST_RESET.equals(intent.getAction())) {
+                if (mAppWidgetHost != null) {
+                    mAppWidgetHost.startListening();
+                }
+            }
+        }
+    };
 
     @Thunk Workspace mWorkspace;
     private View mLauncherView;
@@ -255,8 +261,6 @@
     @Thunk WidgetsContainerView mWidgetsView;
     @Thunk WidgetsModel mWidgetsModel;
 
-    private AppWidgetHostView mQsb;
-
     private Bundle mSavedState;
     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
     // scroll issues (because the workspace may not have been measured yet) and extra work.
@@ -464,7 +468,8 @@
         Selection.setSelection(mDefaultKeySsb, 0);
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        registerReceiver(mCloseSystemDialogsReceiver, filter);
+        filter.addAction(ACTION_APPWIDGET_HOST_RESET);
+        registerReceiver(mUiBroadcastReceiver, filter);
 
         mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
         // In case we are on a device with locked rotation, we should look at preferences to check
@@ -739,10 +744,6 @@
             } 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) {
@@ -1056,7 +1057,6 @@
         if (!isWorkspaceLoading()) {
             getWorkspace().reinflateWidgetsIfNecessary();
         }
-        reinflateQSBIfNecessary();
 
         if (DEBUG_RESUME_TIME) {
             Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
@@ -1368,6 +1368,7 @@
         mWorkspace.setHapticFeedbackEnabled(false);
         mWorkspace.setOnLongClickListener(this);
         mWorkspace.setup(dragController);
+        mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);
         dragController.addDragListener(mWorkspace);
 
         // Get the search/delete/uninstall bar
@@ -1393,7 +1394,6 @@
         dragController.addDropTarget(mWorkspace);
         if (mSearchDropTargetBar != null) {
             mSearchDropTargetBar.setup(this, dragController);
-            mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
         }
         if (mAppInfoDropTargetBar != null) {
             mAppInfoDropTargetBar.setup(this, dragController);
@@ -1999,7 +1999,7 @@
         ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
                 .removeAccessibilityStateChangeListener(this);
 
-        unregisterReceiver(mCloseSystemDialogsReceiver);
+        unregisterReceiver(mUiBroadcastReceiver);
 
         LauncherAnimUtils.onDestroyActivity();
 
@@ -2052,10 +2052,9 @@
             appSearchData = new Bundle();
             appSearchData.putString("source", "launcher-search");
         }
-        Rect sourceBounds = new Rect();
-        if (mSearchDropTargetBar != null) {
-            sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
-        }
+
+        // TODO send proper bounds.
+        Rect sourceBounds = null;
 
         boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
                 appSearchData, sourceBounds);
@@ -2465,19 +2464,6 @@
     }
 
     /**
-     * Re-listen when widget host is reset.
-     */
-    @Override
-    public void onAppWidgetHostReset() {
-        if (mAppWidgetHost != null) {
-            mAppWidgetHost.startListening();
-        }
-
-        // Recreate the QSB, as the widget has been reset.
-        bindSearchProviderChanged();
-    }
-
-    /**
      * Launches the intent referred by the clicked shortcut.
      *
      * @param v The view representing the clicked shortcut.
@@ -3497,105 +3483,6 @@
         return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
     }
 
-    public View getOrCreateQsbBar() {
-        if (launcherCallbacksProvidesSearch()) {
-            return mLauncherCallbacks.getQsbBar();
-        }
-
-        if (mQsb == null) {
-            AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(this);
-            if (searchProvider == null) {
-                return null;
-            }
-
-            Bundle opts = new Bundle();
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
-
-            // Determine the min and max dimensions of the widget.
-            LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile portraitProfile = app.getInvariantDeviceProfile().portraitProfile;
-            DeviceProfile landscapeProfile = app.getInvariantDeviceProfile().landscapeProfile;
-            float density = getResources().getDisplayMetrics().density;
-            Point searchDimens = portraitProfile.getSearchBarDimensForWidgetOpts(getResources());
-            int maxHeight = (int) (searchDimens.y / density);
-            int minHeight = maxHeight;
-            int maxWidth = (int) (searchDimens.x / density);
-            int minWidth = maxWidth;
-            if (!landscapeProfile.isVerticalBarLayout()) {
-                searchDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(getResources());
-                maxHeight = (int) Math.max(maxHeight, searchDimens.y / density);
-                minHeight = (int) Math.min(minHeight, searchDimens.y / density);
-                maxWidth = (int) Math.max(maxWidth, searchDimens.x / density);
-                minWidth = (int) Math.min(minWidth, searchDimens.x / density);
-            }
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth);
-            if (LOGD) {
-                Log.d(TAG, "QSB widget options: maxHeight=" + maxHeight + " minHeight=" + minHeight
-                        + " maxWidth=" + maxWidth + " minWidth=" + minWidth);
-            }
-
-            if (mLauncherCallbacks != null) {
-                opts.putAll(mLauncherCallbacks.getAdditionalSearchWidgetOptions());
-            }
-
-            int widgetId = mSharedPrefs.getInt(QSB_WIDGET_ID, -1);
-            AppWidgetProviderInfo widgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
-            if (!searchProvider.provider.flattenToString().equals(
-                    mSharedPrefs.getString(QSB_WIDGET_PROVIDER, null))
-                    || (widgetInfo == null)
-                    || !widgetInfo.provider.equals(searchProvider.provider)) {
-                // A valid widget is not already bound.
-                if (widgetId > -1) {
-                    mAppWidgetHost.deleteAppWidgetId(widgetId);
-                    widgetId = -1;
-                }
-
-                // Try to bind a new widget
-                widgetId = mAppWidgetHost.allocateAppWidgetId();
-
-                if (!AppWidgetManagerCompat.getInstance(this)
-                        .bindAppWidgetIdIfAllowed(widgetId, searchProvider, opts)) {
-                    mAppWidgetHost.deleteAppWidgetId(widgetId);
-                    widgetId = -1;
-                }
-
-                mSharedPrefs.edit()
-                    .putInt(QSB_WIDGET_ID, widgetId)
-                    .putString(QSB_WIDGET_PROVIDER, searchProvider.provider.flattenToString())
-                    .apply();
-            }
-
-            mAppWidgetHost.setQsbWidgetId(widgetId);
-            if (widgetId != -1) {
-                mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
-                mQsb.setId(R.id.qsb_widget);
-                if (!Utilities.containsAll(
-                        AppWidgetManager.getInstance(this).getAppWidgetOptions(widgetId), opts)) {
-                    // Launcher should not be updating the options often.
-                    FileLog.d(TAG, "Options for QSB were not same");
-                    mQsb.updateAppWidgetOptions(opts);
-                }
-                mQsb.setPadding(0, 0, 0, 0);
-                mSearchDropTargetBar.addView(mQsb);
-                mSearchDropTargetBar.setQsbSearchBar(mQsb);
-            }
-        }
-        return mQsb;
-    }
-
-    private void reinflateQSBIfNecessary() {
-        if (mQsb instanceof LauncherAppWidgetHostView &&
-                ((LauncherAppWidgetHostView) mQsb).isReinflateRequired()) {
-            mSearchDropTargetBar.removeView(mQsb);
-            mQsb = null;
-            mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
-        }
-    }
-
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
@@ -3615,16 +3502,6 @@
     }
 
     /**
-     * Receives notifications when system dialogs are to be closed.
-     */
-    @Thunk class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            closeSystemDialogs();
-        }
-    }
-
-    /**
      * If the activity is currently paused, signal that we need to run the passed Runnable
      * in onResume.
      *
@@ -3729,12 +3606,13 @@
 
     @Override
     public void bindScreens(ArrayList<Long> orderedScreenIds) {
-        bindAddScreens(orderedScreenIds);
-
-        // If there are no screens, we need to have an empty screen
-        if (orderedScreenIds.size() == 0) {
-            mWorkspace.addExtraEmptyScreen();
+        // Make sure the first screen is always at the start.
+        if (orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
+            orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
+            orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
+            mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
         }
+        bindAddScreens(orderedScreenIds);
 
         // Create the custom content page (this call updates mDefaultScreen which calls
         // setCurrentPage() so ensure that all pages are added before calling this).
@@ -3744,11 +3622,14 @@
         }
     }
 
-    @Override
-    public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
+    private void bindAddScreens(ArrayList<Long> orderedScreenIds) {
         int count = orderedScreenIds.size();
         for (int i = 0; i < count; i++) {
-            mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
+            long screenId = orderedScreenIds.get(i);
+            if (screenId != Workspace.FIRST_SCREEN_ID) {
+                // No need to bind the first screen, as its always bound.
+                mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
+            }
         }
     }
 
@@ -3827,24 +3708,6 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                     ShortcutInfo info = (ShortcutInfo) item;
                     view = createShortcut(info);
-
-                    /*
-                     * TODO: FIX collision case
-                     */
-                    if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                        CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
-                        if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
-                            View v = cl.getChildAt(item.cellX, item.cellY);
-                            Object tag = v.getTag();
-                            String desc = "Collision while binding workspace item: " + item
-                                    + ". Collides with " + tag;
-                            if (ProviderConfig.IS_DOGFOOD_BUILD) {
-                                throw (new RuntimeException(desc));
-                            } else {
-                                Log.d(TAG, desc);
-                            }
-                        }
-                    }
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                     view = FolderIcon.fromXml(R.layout.folder_icon, this,
@@ -3855,6 +3718,25 @@
                     throw new RuntimeException("Invalid Item Type");
             }
 
+             /*
+             * Remove colliding items.
+             */
+            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
+                if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
+                    View v = cl.getChildAt(item.cellX, item.cellY);
+                    Object tag = v.getTag();
+                    String desc = "Collision while binding workspace item: " + item
+                            + ". Collides with " + tag;
+                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                        throw (new RuntimeException(desc));
+                    } else {
+                        Log.d(TAG, desc);
+                        LauncherModel.deleteItemFromDatabase(this, item);
+                        continue;
+                    }
+                }
+            }
             workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
                     item.cellY, 1, 1);
             if (animateIcons) {
@@ -4154,17 +4036,6 @@
         return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
     }
 
-    public void bindSearchProviderChanged() {
-        if (mSearchDropTargetBar == null) {
-            return;
-        }
-        if (mQsb != null) {
-            mSearchDropTargetBar.removeView(mQsb);
-            mQsb = null;
-        }
-        mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
-    }
-
     /**
      * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent
      * multiple calls to bind the same list.)
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 9d889e0..ae3abbf 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -105,7 +105,6 @@
         // Register intent receivers
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
         // For handling managed profiles
         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
         filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 8c23ff3..3bb7381 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -37,7 +37,6 @@
 
     private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
 
-    private int mQsbWidgetId = -1;
     private Launcher mLauncher;
 
     public LauncherAppWidgetHost(Launcher launcher, int hostId) {
@@ -45,23 +44,9 @@
         mLauncher = launcher;
     }
 
-    public void setQsbWidgetId(int widgetId) {
-        mQsbWidgetId = widgetId;
-    }
-
     @Override
     protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        if (appWidgetId == mQsbWidgetId) {
-            return new LauncherAppWidgetHostView(context) {
-
-                @Override
-                protected View getErrorView() {
-                    // For the QSB, show an empty view instead of an error view.
-                    return new View(getContext());
-                }
-            };
-        }
         return new LauncherAppWidgetHostView(context);
     }
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 28557d0..53d9c04 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -54,6 +54,8 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mChildrenFocused;
 
+    protected int mErrorViewId = R.layout.appwidget_error;
+
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mContext = context;
@@ -68,7 +70,7 @@
 
     @Override
     protected View getErrorView() {
-        return mInflater.inflate(R.layout.appwidget_error, this, false);
+        return mInflater.inflate(mErrorViewId, this, false);
     }
 
     public void updateLastInflationOrientation() {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 649f42d..557a91a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.app.SearchManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -177,7 +176,6 @@
         public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
                               boolean forceAnimateIcons);
         public void bindScreens(ArrayList<Long> orderedScreenIds);
-        public void bindAddScreens(ArrayList<Long> orderedScreenIds);
         public void finishBindingItems();
         public void bindAppWidget(LauncherAppWidgetInfo info);
         public void bindAllApplications(ArrayList<AppInfo> apps);
@@ -196,7 +194,6 @@
         public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
         public void notifyWidgetProvidersChanged();
         public void bindWidgetsModel(WidgetsModel model);
-        public void bindSearchProviderChanged();
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
         public void executeOnNextDraw(ViewOnDrawExecutor executor);
@@ -1139,11 +1136,6 @@
         if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
             // If we have changed locale we need to clear out the labels in all apps/workspace.
             forceReload();
-        } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) {
-            Callbacks callbacks = getCallback();
-            if (callbacks != null) {
-                callbacks.bindSearchProviderChanged();
-            }
         } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
                 || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
             UserManagerCompat.getInstance(context).enableAndResetCache();
@@ -1528,7 +1520,12 @@
             }
 
             if (!occupied.containsKey(item.screenId)) {
-                occupied.put(item.screenId, new GridOccupancy(countX + 1, countY + 1));
+                GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
+                if (item.screenId == Workspace.FIRST_SCREEN_ID) {
+                    // Mark the first row as occupied in order to account for the QSB.
+                    screen.markCells(0, 0, countX + 1, 1, true);
+                }
+                occupied.put(item.screenId, screen);
             }
             final GridOccupancy occupancy = occupied.get(item.screenId);
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 11d61d0..49ce06a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -611,8 +611,11 @@
             // Database was just created, so wipe any previous widgets
             if (mWidgetHostResetHandler != null) {
                 new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost();
-                mWidgetHostResetHandler.sendEmptyMessage(
-                        ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET);
+                mWidgetHostResetHandler.sendMessage(Message.obtain(
+                        mWidgetHostResetHandler,
+                        ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET,
+                        mContext
+                ));
             }
 
             // Set the flag for empty DB
@@ -1100,7 +1103,11 @@
                         mListener.onExtractedColorsChanged();
                         break;
                     case MSG_APP_WIDGET_HOST_RESET:
-                        mListener.onAppWidgetHostReset();
+                        Context context = (Context) msg.obj;
+                        if (context != null) {
+                            context.sendBroadcast(new Intent(Launcher.ACTION_APPWIDGET_HOST_RESET)
+                                    .setPackage(context.getPackageName()));
+                        }
                         break;
                 }
             }
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 524befc..5998dad 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -10,6 +10,4 @@
     public void onLauncherProviderChange();
 
     public void onExtractedColorsChanged();
-
-    public void onAppWidgetHostReset();
 }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index fdca9f2..4af53d2 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1653,7 +1653,8 @@
                 if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
 
                 final int pageUnderPointIndex = getNearestHoverOverPageIndex();
-                if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView)) {
+                // Do not allow any page to be moved to 0th position.
+                if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) {
                     mTempVisiblePagesRange[0] = 0;
                     mTempVisiblePagesRange[1] = getPageCount() - 1;
                     getFreeScrollPageRange(mTempVisiblePagesRange);
@@ -2171,7 +2172,8 @@
     public boolean startReordering(View v) {
         int dragViewIndex = indexOfChild(v);
 
-        if (mTouchState != TOUCH_STATE_REST || dragViewIndex == -1) return false;
+        // Do not allow the first page to be moved around
+        if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false;
 
         mTempVisiblePagesRange[0] = 0;
         mTempVisiblePagesRange[1] = getPageCount() - 1;
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index c8c8fa4..c1c2519 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -150,12 +150,10 @@
                 animateOverviewPanelButtons(goingTowards == OVERVIEW);
             } else if (startState == NORMAL) {
                 animateHotseatAndPageIndicator(goingTowards == NORMAL);
-                animateQsb(goingTowards == NORMAL);
             }
         } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
             if (startState == OVERVIEW) {
                 animateHotseatAndPageIndicator(goingTowards == NORMAL);
-                animateQsb(goingTowards == NORMAL);
                 animateScrim(goingTowards == OVERVIEW);
             } else if (startState == NORMAL) {
                 animateOverviewPanelButtons(goingTowards == OVERVIEW);
@@ -198,12 +196,6 @@
         }
     }
 
-    private void animateQsb(boolean show) {
-        SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR
-                : SearchDropTargetBar.State.INVISIBLE;
-        mLauncher.getSearchDropTargetBar().animateToState(searchBarState, THRESHOLD_ANIM_DURATION);
-    }
-
     private void animateOverviewPanelButtons(boolean show) {
         animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
     }
diff --git a/src/com/android/launcher3/QsbContainerView.java b/src/com/android/launcher3/QsbContainerView.java
new file mode 100644
index 0000000..0a112d2
--- /dev/null
+++ b/src/com/android/launcher3/QsbContainerView.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.SearchManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
+/**
+ * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
+ * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
+ */
+public class QsbContainerView extends FrameLayout {
+
+    private boolean mBound;
+
+    public QsbContainerView(Context context) {
+        super(context);
+    }
+
+    public QsbContainerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public QsbContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (!mBound) {
+            FragmentManager fm = ((Launcher) getContext()).getFragmentManager();
+            fm.beginTransaction().add(R.id.qsb_container, new QsbFragment()).commit();
+            mBound = true;
+        }
+    }
+
+    @Override
+    public void setPadding(int left, int top, int right, int bottom) {
+        super.setPadding(0, 0, 0, 0);
+    }
+
+    /**
+     * A fragment to display the QSB.
+     */
+    public static class QsbFragment extends Fragment implements View.OnClickListener {
+
+        private static final int REQUEST_BIND_QSB = 1;
+        private static final String QSB_WIDGET_ID = "qsb_widget_id";
+
+        private static int sSavedWidgetId = -1;
+
+        private AppWidgetProviderInfo mWidgetInfo;
+        private LauncherAppWidgetHostView mQsb;
+
+        private BroadcastReceiver mRebindReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                rebindFragment();
+            }
+        };
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            IntentFilter filter = new IntentFilter(Launcher.ACTION_APPWIDGET_HOST_RESET);
+            filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+            getContext().registerReceiver(mRebindReceiver, filter);
+        }
+
+        @Override
+        public View onCreateView(
+                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+            if (savedInstanceState != null) {
+                sSavedWidgetId = savedInstanceState.getInt(QSB_WIDGET_ID, -1);
+            }
+
+            Launcher launcher = (Launcher) getActivity();
+            mWidgetInfo = getSearchWidgetProvider(launcher);
+            if (mWidgetInfo == null) {
+                // There is no search provider, just show the default widget.
+                return getDefaultView(inflater, container, false);
+            }
+
+            SharedPreferences prefs = Utilities.getPrefs(launcher);
+            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(launcher);
+            LauncherAppWidgetHost widgetHost = launcher.getAppWidgetHost();
+            InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+            Bundle opts = new Bundle();
+            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(launcher, idp.numColumns, 1, null);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+
+            int widgetId = prefs.getInt(QSB_WIDGET_ID, -1);
+            AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
+            boolean isWidgetBound = (widgetInfo != null) &&
+                    widgetInfo.provider.equals(mWidgetInfo.provider);
+
+            if (!isWidgetBound) {
+                // widgetId is already bound and its not the correct provider.
+                // Delete the widget id.
+                if (widgetId > -1) {
+                    widgetHost.deleteAppWidgetId(widgetId);
+                    widgetId = -1;
+                }
+
+                widgetId = widgetHost.allocateAppWidgetId();
+                isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
+                if (!isWidgetBound) {
+                    widgetHost.deleteAppWidgetId(widgetId);
+                    widgetId = -1;
+                }
+            }
+
+            if (isWidgetBound) {
+                mQsb = (LauncherAppWidgetHostView)
+                        widgetHost.createView(launcher, widgetId, mWidgetInfo);
+                mQsb.setId(R.id.qsb_widget);
+                mQsb.mErrorViewId = R.layout.qsb_default_view;
+
+                if (!Utilities.containsAll(AppWidgetManager.getInstance(launcher)
+                        .getAppWidgetOptions(widgetId), opts)) {
+                    mQsb.updateAppWidgetOptions(opts);
+                }
+                mQsb.setPadding(0, 0, 0, 0);
+                return mQsb;
+            }
+
+            // Return a default widget with setup icon.
+            return getDefaultView(inflater, container, true);
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (view.getId() == R.id.btn_qsb_search) {
+                getActivity().startSearch("", false, null, true);
+            } else if (view.getId() == R.id.btn_qsb_setup) {
+                // Allocate a new widget id for QSB
+                sSavedWidgetId = ((Launcher) getActivity())
+                        .getAppWidgetHost().allocateAppWidgetId();
+                // Start intent for bind the widget
+                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, sSavedWidgetId);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider);
+                startActivityForResult(intent, REQUEST_BIND_QSB);
+            }
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle outState) {
+            super.onSaveInstanceState(outState);
+            outState.putInt(QSB_WIDGET_ID, sSavedWidgetId);
+        }
+
+        @Override
+        public void onActivityResult(int requestCode, int resultCode, Intent data) {
+            if (requestCode == REQUEST_BIND_QSB) {
+                if (resultCode == Activity.RESULT_OK) {
+                    int widgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                            sSavedWidgetId);
+                    Utilities.getPrefs(getContext()).edit().putInt(QSB_WIDGET_ID, widgetId).apply();
+                    sSavedWidgetId = -1;
+                    rebindFragment();
+                } else if (sSavedWidgetId != -1) {
+                    ((Launcher) getActivity()).getAppWidgetHost().deleteAppWidgetId(sSavedWidgetId);
+                    sSavedWidgetId = -1;
+                }
+            }
+        }
+
+        @Override
+        public void onResume() {
+            super.onResume();
+            if (mQsb != null && mQsb.isReinflateRequired()) {
+                rebindFragment();
+            }
+        }
+
+        @Override
+        public void onDestroy() {
+            getContext().unregisterReceiver(mRebindReceiver);
+            super.onDestroy();
+        }
+
+        private void rebindFragment() {
+            if (getActivity() != null) {
+                // Recreate the fragment. This will cause the qsb to be inflated again.
+                getActivity().getFragmentManager().beginTransaction()
+                        .replace(R.id.qsb_container, new QsbFragment()).commit();
+            }
+        }
+
+        private View getDefaultView(LayoutInflater inflater, ViewGroup parent, boolean showSetup) {
+            View v = inflater.inflate(R.layout.qsb_default_view, parent, false);
+            if (showSetup) {
+                View setupButton = v.findViewById(R.id.btn_qsb_setup);
+                setupButton.setVisibility(View.VISIBLE);
+                setupButton.setOnClickListener(this);
+            }
+            v.findViewById(R.id.btn_qsb_search).setOnClickListener(this);
+            return v;
+        }
+    }
+
+    /**
+     * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
+     * provided by the same package which is set to be global search activity.
+     * If widgetCategory is not supported, or no such widget is found, returns the first widget
+     * provided by the package.
+     */
+    public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
+        SearchManager searchManager =
+                (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+        ComponentName searchComponent = searchManager.getGlobalSearchActivity();
+        if (searchComponent == null) return null;
+        String providerPkg = searchComponent.getPackageName();
+
+        AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+
+        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+        for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
+            if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
+                if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
+                    return info;
+                } else if (defaultWidgetForSearchPackage == null) {
+                    defaultWidgetForSearchPackage = info;
+                }
+            }
+        }
+        return defaultWidgetForSearchPackage;
+    }
+}
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 171dd87..e43e96c 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -26,8 +24,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewDebug;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.dragndrop.DragController;
@@ -39,32 +35,21 @@
  */
 public class SearchDropTargetBar extends BaseDropTargetBar {
 
-    private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
-    private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
-
     /** The different states that the search bar space can be in. */
     public enum State {
-        INVISIBLE             (0f, 0f, 0f),
-        INVISIBLE_TRANSLATED  (0f, 0f, -1f),
-        SEARCH_BAR            (1f, 0f, 0f),
-        DROP_TARGET           (0f, 1f, 0f);
+        INVISIBLE             (0f),
+        DROP_TARGET           (1f);
 
-        private final float mSearchBarAlpha;
         private final float mDropTargetBarAlpha;
-        private final float mTranslation;
 
-        State(float sbAlpha, float dtbAlpha, float translation) {
-            mSearchBarAlpha = sbAlpha;
+        State(float dtbAlpha) {
             mDropTargetBarAlpha = dtbAlpha;
-            mTranslation = translation;
         }
-
     }
 
 
     @ViewDebug.ExportedProperty(category = "launcher")
-    private State mState = State.SEARCH_BAR;
-    @Thunk View mQSB;
+    private State mState = State.INVISIBLE;
 
     // Drop targets
     private ButtonDropTarget mDeleteDropTarget;
@@ -113,11 +98,7 @@
 
     @Override
     public void hideDropTargets() {
-        animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
-    }
-
-    public void setQsbSearchBar(View qsb) {
-        mQSB = qsb;
+        animateToState(State.INVISIBLE, DEFAULT_DRAG_FADE_DURATION);
     }
 
     /**
@@ -140,30 +121,6 @@
                 AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
             }
 
-            if (mQSB != null) {
-                boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
-                        .isVerticalBarLayout();
-                float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
-
-                if (duration > 0) {
-                    int translationChange = Float.compare(mQSB.getTranslationY(), translation);
-
-                    animateAlpha(mQSB, mState.mSearchBarAlpha,
-                            translationChange == 0 ? DEFAULT_INTERPOLATOR
-                                    : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
-                                    : MOVE_UP_INTERPOLATOR));
-
-                    if (translationChange != 0) {
-                        mCurrentAnimation.play(
-                                ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
-                    }
-                } else {
-                    mQSB.setTranslationY(translation);
-                    mQSB.setAlpha(mState.mSearchBarAlpha);
-                    AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
-                }
-            }
-
             // Start the final animation
             if (duration > 0) {
                 if (animation != null) {
@@ -175,30 +132,8 @@
         }
     }
 
-    /**
-     * @return the bounds of the QSB search bar.
-     */
-    public Rect getSearchBarBounds() {
-        if (mQSB != null) {
-            final int[] pos = new int[2];
-            mQSB.getLocationOnScreen(pos);
-
-            final Rect rect = new Rect();
-            rect.left = pos[0];
-            rect.top = pos[1];
-            rect.right = pos[0] + mQSB.getWidth();
-            rect.bottom = pos[1] + mQSB.getHeight();
-            return rect;
-        } else {
-            return null;
-        }
-    }
-
     @Override
     public void enableAccessibleDrag(boolean enable) {
-        if (mQSB != null) {
-            mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
-        }
         mDeleteDropTarget.enableAccessibleDrag(enable);
         mUninstallDropTarget.enableAccessibleDrag(enable);
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c9d8cce..4aaa02f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,10 +18,7 @@
 
 import android.annotation.TargetApi;
 import android.app.Activity;
-import android.app.SearchManager;
 import android.app.WallpaperManager;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -603,39 +600,6 @@
     }
 
     /**
-     * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
-     * provided by the same package which is set to be global search activity.
-     * If widgetCategory is not supported, or no such widget is found, returns the first widget
-     * provided by the package.
-     */
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
-        SearchManager searchManager =
-                (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
-        ComponentName searchComponent = searchManager.getGlobalSearchActivity();
-        if (searchComponent == null) return null;
-        String providerPkg = searchComponent.getPackageName();
-
-        AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
-
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-        for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
-            if (info.provider.getPackageName().equals(providerPkg)) {
-                if (ATLEAST_JB_MR1) {
-                    if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
-                        return info;
-                    } else if (defaultWidgetForSearchPackage == null) {
-                        defaultWidgetForSearchPackage = info;
-                    }
-                } else {
-                    return info;
-                }
-            }
-        }
-        return defaultWidgetForSearchPackage;
-    }
-
-    /**
      * Compresses the bitmap to a byte array for serialization.
      */
     public static byte[] flattenBitmap(Bitmap bitmap) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0f0673a..6f81f59 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -73,7 +73,6 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.LongArrayMap;
@@ -109,7 +108,10 @@
     private static final boolean MAP_RECURSE = true;
 
     // The screen id used for the empty screen always present to the right.
-    public final static long EXTRA_EMPTY_SCREEN_ID = -201;
+    public static final long EXTRA_EMPTY_SCREEN_ID = -201;
+    // The is the first screen. It is always present, even if its empty.
+    public static final long FIRST_SCREEN_ID = 0;
+
     private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
 
     private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
@@ -180,8 +182,8 @@
     // in all apps or customize mode)
 
     enum State {
-        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR, false, false),
-        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false, false),
+        NORMAL          (SearchDropTargetBar.State.INVISIBLE, false, false),
+        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE, false, false),
         SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET, false, true),
         OVERVIEW        (SearchDropTargetBar.State.INVISIBLE, true, true),
         OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true, false);
@@ -501,6 +503,32 @@
         return mTouchState != TOUCH_STATE_REST;
     }
 
+    /**
+     * Initializes and binds the first page
+     * @param qsb an exisitng qsb to recycle or null.
+     */
+    public void bindAndInitFirstWorkspaceScreen(View qsb) {
+        // Add the first page
+        CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
+
+        if (qsb == null) {
+            // Always add a QSB on the first screen.
+            qsb = mLauncher.getLayoutInflater().inflate(R.layout.qsb_container,
+                    firstPage, false);
+        }
+
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) qsb.getLayoutParams();
+        lp.cellX = 0;
+        lp.cellY = 0;
+        lp.cellHSpan = firstPage.getCountX();
+        lp.cellVSpan = 1;
+        lp.canReorder = false;
+
+        if (!firstPage.addViewToCellLayout(qsb, 0, R.id.qsb_container, lp, true)) {
+            Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
+        }
+    }
+
     public void removeAllWorkspaceScreens() {
         // Disable all layout transitions before removing all pages to ensure that we don't get the
         // transition animations competing with us changing the scroll when we add pages or the
@@ -513,30 +541,39 @@
             removeCustomContentPage();
         }
 
+        // Recycle the QSB widget
+        View qsb = findViewById(R.id.qsb_container);
+        if (qsb != null) {
+            ((ViewGroup) qsb.getParent()).removeView(qsb);
+        }
+
         // Remove the pages and clear the screen models
         removeAllViews();
         mScreenOrder.clear();
         mWorkspaceScreens.clear();
 
+        // Ensure that the first page is always present
+        bindAndInitFirstWorkspaceScreen(qsb);
+
         // Re-enable the layout transitions
         enableLayoutTransitions();
     }
 
-    public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
+    public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
         // Find the index to insert this view into.  If the empty screen exists, then
         // insert it before that.
         int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
         if (insertIndex < 0) {
             insertIndex = mScreenOrder.size();
         }
-        return insertNewWorkspaceScreen(screenId, insertIndex);
+        insertNewWorkspaceScreen(screenId, insertIndex);
     }
 
-    public long insertNewWorkspaceScreen(long screenId) {
-        return insertNewWorkspaceScreen(screenId, getChildCount());
+    public void insertNewWorkspaceScreen(long screenId) {
+        insertNewWorkspaceScreen(screenId, getChildCount());
     }
 
-    public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
+    public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
         if (mWorkspaceScreens.containsKey(screenId)) {
             throw new RuntimeException("Screen id " + screenId + " already exists!");
         }
@@ -558,7 +595,8 @@
         if (delegate != null && delegate.isInAccessibleDrag()) {
             newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
         }
-        return screenId;
+
+        return newScreen;
     }
 
     public void createCustomContentContainer() {
@@ -860,7 +898,8 @@
         for (int i = 0; i < total; i++) {
             long id = mWorkspaceScreens.keyAt(i);
             CellLayout cl = mWorkspaceScreens.valueAt(i);
-            if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
+            // FIRST_SCREEN_ID can never be removed.
+            if (id > FIRST_SCREEN_ID && cl.getShortcutsAndWidgets().getChildCount() == 0) {
                 removeScreens.add(id);
             }
         }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index f900790..dd11bde 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -22,6 +22,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.backup.nano.BackupProtos;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
@@ -221,7 +222,7 @@
                 // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
                 // break the loop and abort migration by throwing an exception.
                 OptimalPlacementSolution placement = new OptimalPlacementSolution(
-                        new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), true);
+                        new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
                 placement.find();
                 if (placement.finalPlacedItems.size() > 0) {
                     long newScreenId = LauncherSettings.Settings.call(
@@ -262,13 +263,16 @@
      * Migrate a particular screen id.
      * Strategy:
      *   1) For all possible combinations of row and column, pick the one which causes the least
-     *      data loss: {@link #tryRemove(int, int, ArrayList, float[])}
+     *      data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
      *   2) Maintain a list of all lost items before this screen, and add any new item lost from
      *      this screen to that list as well.
      *   3) If all those items from the above list can be placed on this screen, place them
      *      (otherwise they are placed on a new screen).
      */
     private void migrateScreen(long screenId) {
+        // If we are migrating the first screen, do not touch the first row.
+        int startY = screenId == Workspace.FIRST_SCREEN_ID ? 1 : 0;
+
         ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
 
         int removedCol = Integer.MAX_VALUE;
@@ -286,10 +290,10 @@
 
         // Try removing all possible combinations
         for (int x = 0; x < mSrcX; x++) {
-            for (int y = 0; y < mSrcY; y++) {
+            for (int y = startY; y < mSrcY; y++) {
                 // Use a deep copy when trying out a particular combination as it can change
                 // the underlying object.
-                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss);
+                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss);
 
                 if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
                     removeWt = outLoss[0];
@@ -338,12 +342,13 @@
         if (!mCarryOver.isEmpty() && removeWt == 0) {
             // No new items were removed in this step. Try placing all the items on this screen.
             GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
+            occupied.markCells(0, 0, mTrgX, startY, true);
             for (DbEntry item : finalItems) {
                 occupied.markCells(item, true);
             }
 
             OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
-                    deepCopy(mCarryOver), true);
+                    deepCopy(mCarryOver), startY, true);
             placement.find();
             if (placement.lowestWeightLoss == 0) {
                 // All items got placed
@@ -375,9 +380,10 @@
      * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
      * with the overall item movement.
      */
-    private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
-            float[] outLoss) {
+    private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
+            ArrayList<DbEntry> items, float[] outLoss) {
         GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
+        occupied.markCells(0, 0, mTrgX, startY, true);
 
         col = mShouldRemoveX ? col : Integer.MAX_VALUE;
         row = mShouldRemoveY ? row : Integer.MAX_VALUE;
@@ -399,7 +405,8 @@
             }
         }
 
-        OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
+        OptimalPlacementSolution placement =
+                new OptimalPlacementSolution(occupied, removedItems, startY);
         placement.find();
         finalItems.addAll(placement.finalPlacedItems);
         outLoss[0] = placement.lowestWeightLoss;
@@ -415,19 +422,24 @@
         // linear placement.
         private final boolean ignoreMove;
 
+        // The first row in the grid from where the placement should start.
+        private final int startY;
+
         float lowestWeightLoss = Float.MAX_VALUE;
         float lowestMoveCost = Float.MAX_VALUE;
         ArrayList<DbEntry> finalPlacedItems;
 
-        public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace) {
-            this(occupied, itemsToPlace, false);
+        public OptimalPlacementSolution(
+                GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) {
+            this(occupied, itemsToPlace, startY, false);
         }
 
         public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
-                boolean ignoreMove) {
+                int startY, boolean ignoreMove) {
             this.occupied = occupied;
             this.itemsToPlace = itemsToPlace;
             this.ignoreMove = ignoreMove;
+            this.startY = startY;
 
             // Sort the items such that larger widgets appear first followed by 1x1 items
             Collections.sort(this.itemsToPlace);
@@ -477,7 +489,7 @@
                 int myW = me.spanX;
                 int myH = me.spanY;
 
-                for (int y = 0; y < mTrgY; y++) {
+                for (int y = startY; y < mTrgY; y++) {
                     for (int x = 0; x < mTrgX; x++) {
                         float newMoveCost = moveCost;
                         if (x != myX) {
@@ -547,7 +559,7 @@
                 int newDistance = Integer.MAX_VALUE;
                 int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
 
-                for (int y = 0; y < mTrgY; y++) {
+                for (int y = startY; y < mTrgY; y++) {
                     for (int x = 0; x < mTrgX; x++) {
                         if (!occupied.cells[x][y]) {
                             int dist = ignoreMove ? 0 :
diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java
index 3f5f0b4..6a10b0a 100644
--- a/src/com/android/launcher3/util/GridOccupancy.java
+++ b/src/com/android/launcher3/util/GridOccupancy.java
@@ -77,7 +77,7 @@
     public void markCells(int cellX, int cellY, int spanX, int spanY, boolean value) {
         if (cellX < 0 || cellY < 0) return;
         for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
-            for (int y = cellY; y < cellY + spanY && y < mCountX; y++) {
+            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
                 cells[x][y] = value;
             }
         }
diff --git a/tests/src/com/android/launcher3/BindWidgetTest.java b/tests/src/com/android/launcher3/BindWidgetTest.java
index 81cb8b5..5c5069f 100644
--- a/tests/src/com/android/launcher3/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/BindWidgetTest.java
@@ -205,10 +205,13 @@
      */
     private void setupAndVerifyContents(
             LauncherAppWidgetInfo item, Class<?> widgetClass, String desc) {
-        // Add new screen
-        long screenId = LauncherSettings.Settings.call(
-                mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+        long screenId = Workspace.FIRST_SCREEN_ID;
+        // Update the screen id counter for the provider.
+        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+        if (screenId > Workspace.FIRST_SCREEN_ID) {
+            screenId = Workspace.FIRST_SCREEN_ID;
+        }
         ContentValues v = new ContentValues();
         v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
         v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, 0);
@@ -263,7 +266,7 @@
         item.minSpanY = info.minSpanY;
         item.user = mWidgetManager.getUser(info);
         item.cellX = 0;
-        item.cellY = 0;
+        item.cellY = 1;
         item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
 
         if (bindWidget) {
@@ -319,7 +322,7 @@
         item.minSpanX = 2;
         item.minSpanY = 2;
         item.cellX = 0;
-        item.cellY = 0;
+        item.cellY = 1;
         item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
         return item;
     }
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index ccd8f73..ebf06ba 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -208,6 +208,57 @@
         }});
     }
 
+    public void testWorkspace_first_row_blocked() throws Exception {
+        // The first screen has one item on the 4th column which needs moving, as the first row
+        // will be kept empty.
+        long[][][] ids = createGrid(new int[][][]{{
+                { -1, -1, -1, -1},
+                {  3,  1,  7,  0},
+                {  8,  7,  7, -1},
+                {  5,  2,  7, -1},
+        }}, 0);
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+                new Point(4, 4), new Point(3, 4)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new long[][][] {{
+                {          -1,           -1,           -1},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
+                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+        }, {
+                {ids[0][1][3]},
+        }});
+    }
+
+    public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
+        // Items will get moved to the next screen to keep the first screen empty.
+        long[][][] ids = createGrid(new int[][][]{{
+                { -1, -1, -1, -1},
+                {  0,  1,  0,  0},
+                {  8,  7,  7, -1},
+                {  5,  6,  7, -1},
+        }}, 0);
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new long[][][] {{
+                {          -1,           -1,           -1},
+                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+        }, {
+                {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
+                {ids[0][1][3]},
+        }});
+    }
+
+    private long[][][] createGrid(int[][][] typeArray) throws Exception {
+        return createGrid(typeArray, 1);
+    }
+
     /**
      * Initializes the DB with dummy elements to represent the provided grid structure.
      * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
@@ -215,14 +266,18 @@
      *                  two represent the workspace grid.
      * @return the same grid representation where each entry is the corresponding item id.
      */
-    private long[][][] createGrid(int[][][] typeArray) throws Exception {
+    private long[][][] createGrid(int[][][] typeArray, long startScreen) throws Exception {
+        LauncherSettings.Settings.call(getMockContentResolver(),
+                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
         long[][][] ids = new long[typeArray.length][][];
 
         for (int i = 0; i < typeArray.length; i++) {
             // Add screen to DB
-            long screenId = LauncherSettings.Settings.call(getMockContentResolver(),
-                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                    .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+            long screenId = startScreen + i;
+
+            // Keep the screen id counter up to date
+            LauncherSettings.Settings.call(getMockContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
 
             ContentValues v = new ContentValues();
             v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
@@ -270,7 +325,8 @@
                     } else {
                         assertEquals(1, c.getCount());
                         c.moveToNext();
-                        assertEquals(id, c.getLong(0));
+                        assertEquals(String.format("Failed to verify item ad %d %d, %d", i, y, x),
+                                id, c.getLong(0));
                         total++;
                     }
                     c.close();