Merge tag 'android-15.0.0_r32' of https://android.googlesource.com/platform/packages/apps/Launcher3 into HEAD

Android 15.0.0 Release 32 (BP1A.250505.005)

Change-Id: I6986c8f9627036a5c4d883fbf64e0bd4c6e41e11
diff --git a/Android.bp b/Android.bp
index 73d0fce..10ed01f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -397,6 +397,7 @@
         "kotlinx_coroutines",
         "com_android_launcher3_flags_lib",
         "com_android_wm_shell_flags_lib",
+        "OmniPreferenceTheme",
         "dagger2",
         "jsr330",
         "com_android_systemui_shared_flags_lib",
@@ -518,12 +519,13 @@
 
 // Library with all the source code and dependencies for building Quickstep
 android_library {
-    name: "Launcher3QuickStepLib",
+    name: "Launcher3QuickStepLibGoogle",
     defaults: [
         "launcher_compose_defaults",
         "quickstep_compose_defaults",
     ],
     srcs: [
+        "src_overlay/google/com/android/launcher3/overlay/OverlayCallbackImpl.java",
         ":launcher-src",
         ":launcher-quickstep-src",
         ":launcher-quickstep-dagger",
@@ -538,6 +540,43 @@
     // in QuickstepResLib to take precendece, so it should be the final
     // dependency. See b/208647810 for how this can go wrong.
     static_libs: [
+        "OmniLib",
+        "//vendor/omni:libGoogleFeed",
+        "SystemUI-statsd",
+        "QuickstepResLib",
+    ],
+    manifest: "quickstep/AndroidManifest.xml",
+    platform_apis: true,
+    plugins: ["dagger2-compiler"],
+    min_sdk_version: "current",
+    // TODO(b/319712088): re-enable use_resource_processor
+    use_resource_processor: false,
+}
+
+android_library {
+    name: "Launcher3QuickStepLibMock",
+    defaults: [
+        "launcher_compose_defaults",
+        "quickstep_compose_defaults",
+    ],
+    srcs: [
+        "src_overlay/mock/com/android/launcher3/overlay/OverlayCallbackImpl.java",
+        "src_overlay/mock/com/google/android/libraries/gsa/launcherclient/LauncherClientCallbacks.java",
+        ":launcher-src",
+        ":launcher-quickstep-src",
+        ":launcher-quickstep-dagger",
+        ":launcher-build-config",
+    ],
+    resource_dirs: [],
+    libs: [
+        "framework-statsd.stubs.module_lib",
+    ],
+    // Note the ordering here is important when it comes to resource
+    // overriding. We want the most specific resource overrides defined
+    // in QuickstepResLib to take precendece, so it should be the final
+    // dependency. See b/208647810 for how this can go wrong.
+    static_libs: [
+        "OmniLib",
         "SystemUI-statsd",
         "QuickstepResLib",
     ],
@@ -552,7 +591,7 @@
 // Build rule for Quickstep app.
 android_app {
     name: "Launcher3QuickStep",
-    static_libs: ["Launcher3QuickStepLib"],
+    defaults: ["omni_launcher3_defaults"],
     optimize: {
         proguard_flags_files: [":launcher-proguard-rules"],
         enabled: true,
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 517bd6d..89291c1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -67,6 +67,9 @@
             <meta-data
                 android:name="com.android.launcher3.grid.control"
                 android:value="${packageName}.grid_control" />
+            <meta-data
+                android:name="com.android.launcher3.themedicon.option"
+                android:value="${packageName}.grid_control" />
         </activity>
 
     </application>
diff --git a/go/AndroidManifest-launcher.xml b/go/AndroidManifest-launcher.xml
index 2223036..c7562f1 100644
--- a/go/AndroidManifest-launcher.xml
+++ b/go/AndroidManifest-launcher.xml
@@ -65,6 +65,9 @@
             <meta-data
                 android:name="com.android.launcher3.grid.control"
                 android:value="${packageName}.grid_control" />
+            <meta-data
+                android:name="com.android.launcher3.themedicon.option"
+                android:value="${packageName}.grid_control" />
         </activity>
 
     </application>
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 80d8154..813ef75 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -65,6 +65,9 @@
             <meta-data
                 android:name="com.android.launcher3.grid.control"
                 android:value="${packageName}.grid_control" />
+            <meta-data
+                android:name="com.android.launcher3.themedicon.option"
+                android:value="${packageName}.grid_control" />
         </activity>
 
     </application>
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index fcd2e54..f16b7de 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -36,6 +36,21 @@
             android:theme="@style/ThemeControlHighlightWorkspaceColor" />
 
         <Button
+            style="@style/OverviewActionButton"
+            android:id="@+id/action_clear_all"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/recents_clear_all"
+            android:theme="@style/ThemeControlHighlightWorkspaceColor"
+            android:drawableStart="@drawable/ic_close_all" />
+
+        <Space
+            android:id="@+id/action_split_space"
+            android:layout_width="@dimen/overview_actions_button_spacing"
+            android:layout_height="1dp"
+            android:visibility="gone" />
+
+        <Button
             android:id="@+id/action_split"
             style="@style/OverviewActionButton"
             android:layout_width="wrap_content"
diff --git a/quickstep/res/values/custom_strings.xml b/quickstep/res/values/custom_strings.xml
new file mode 100644
index 0000000..f8993a6
--- /dev/null
+++ b/quickstep/res/values/custom_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2017 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recents_clear">Clear</string>
+</resources>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index b51409c..622826d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -197,7 +197,7 @@
         }
 
         // TODO: Disable touch events on QSB otherwise it can crash.
-        mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+        mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat_taskbar, this, false);
 
         mNumStaticViews = taskbarRecentsLayoutTransition() && !mActivityContext.isPhoneMode()
                 ? addStaticViews() : 0;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 999b13e..6db202c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -131,6 +131,7 @@
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.overlay.OverlayCallbackImpl;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.proxy.ProxyActivityStarter;
 import com.android.launcher3.statehandlers.DepthController;
@@ -192,6 +193,7 @@
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.animation.back.FlingOnBackAnimationCallback;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
@@ -271,6 +273,11 @@
     }
 
     @Override
+    protected LauncherOverlayManager getDefaultOverlay() {
+        return new OverlayCallbackImpl(this);
+    }
+
+    @Override
     protected void setupViews() {
         super.setupViews();
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 932d241..eeb6223 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -45,7 +45,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return OVERVIEW_ACTIONS | CLEAR_ALL_BUTTON;
+        return OVERVIEW_ACTIONS;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index c48ba4f..19c8399 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -110,7 +110,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        int elements = CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+        int elements = OVERVIEW_ACTIONS;
         DeviceProfile dp = launcher.getDeviceProfile();
         boolean showFloatingSearch;
         if (dp.isPhone) {
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index ff9c9f6..e623ed0 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -120,7 +120,9 @@
             TaskShortcutFactory.WELLBEING,
             TaskShortcutFactory.SAVE_APP_PAIR,
             TaskShortcutFactory.SCREENSHOT,
-            TaskShortcutFactory.MODAL
+            TaskShortcutFactory.MODAL,
+            TaskShortcutFactory.CLEAR_TASK,
+            TaskShortcutFactory.CLEAR_ALL_TASK
     };
 
     /**
@@ -229,6 +231,12 @@
             }
         }
 
+        private void clearAllTasks() {
+            RecentsView recentsView =
+                    mTaskContainer.getThumbnailViewDeprecated().getTaskView().getRecentsView();
+            recentsView.dismissAllTasks(null);
+        }
+
         protected void enterSplitSelect() {
             RecentsView overviewPanel = mTaskContainer.getTaskView().getRecentsView();
             // Task has already been dismissed
@@ -395,6 +403,10 @@
             public void onSaveAppPair() {
                 endLiveTileMode(TaskOverlay.this::saveAppPair);
             }
+
+            public void onClearAllTasks() {
+                endLiveTileMode(TaskOverlay.this::clearAllTasks);
+            }
         }
     }
 
@@ -411,5 +423,7 @@
 
         /** User wants to save an app pair with current group of apps. */
         void onSaveAppPair();
+
+        void onClearAllTasks();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 785666f..f8012d1 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -508,4 +508,59 @@
             return createSingletonShortcutList(modalStateSystemShortcut);
         }
     };
+
+    TaskShortcutFactory CLEAR_TASK = new TaskShortcutFactory() {
+        @Override
+        public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
+                TaskContainer taskContainer) {
+            return Collections.singletonList(new ClearTaskSystemShortcut(container, taskContainer));
+        }
+    };
+
+    class ClearTaskSystemShortcut extends SystemShortcut<ActivityContext> {
+
+        private static final String TAG = "ClearTaskSystemShortcut";
+
+        private final TaskView mTaskView;
+
+        public ClearTaskSystemShortcut(ActivityContext target, TaskContainer taskContainer) {
+            super(R.drawable.ic_close, R.string.recents_clear, target, taskContainer.getItemInfo(),
+                    taskContainer.getTaskView());
+            mTaskView = taskContainer.getTaskView();
+        }
+
+        @Override
+        public void onClick(View view) {
+            dismissTaskMenuView();
+            mTaskView.getRecentsView().dismissTask(mTaskView, true /*animateTaskView*/,
+                    true /*removeTask*/);
+        }
+    }
+
+    TaskShortcutFactory CLEAR_ALL_TASK = new TaskShortcutFactory() {
+        @Override
+        public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
+                TaskContainer taskContainer) {
+            return Collections.singletonList(new ClearAllTaskSystemShortcut(container, taskContainer));
+        }
+    };
+
+    class ClearAllTaskSystemShortcut extends SystemShortcut<ActivityContext> {
+
+        private static final String TAG = "ClearAllTaskSystemShortcut";
+
+        private final TaskView mTaskView;
+
+        public ClearAllTaskSystemShortcut(ActivityContext target, TaskContainer taskContainer) {
+            super(R.drawable.ic_close_all, R.string.recents_clear_all, target, taskContainer.getItemInfo(),
+                    taskContainer.getTaskView());
+            mTaskView = taskContainer.getTaskView();
+        }
+
+        @Override
+        public void onClick(View view) {
+            dismissTaskMenuView();
+            mTaskView.getRecentsView().dismissAllTasks(null);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index c2e7536..b2cb7b1 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -117,7 +117,7 @@
      * For this state, whether clear all button should be shown.
      */
     public boolean hasClearAllButton() {
-        return hasFlag(FLAG_CLEAR_ALL_BUTTON);
+        return false;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 4a2be2a..160a511 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -181,6 +181,8 @@
         mSplitButton = findViewById(R.id.action_split);
         mSplitButton.setOnClickListener(this);
         mSaveAppPairButton.setOnClickListener(this);
+        View clearallButton = findViewById(R.id.action_clear_all);
+        clearallButton.setOnClickListener(this);
     }
 
     /**
@@ -204,6 +206,8 @@
             mCallbacks.onSplit();
         } else if (id == R.id.action_save_app_pair) {
             mCallbacks.onSaveAppPair();
+        } else if (id == R.id.action_clear_all) {
+            mCallbacks.onClearAllTasks();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 6752775..4760654 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4527,7 +4527,7 @@
     }
 
     @SuppressWarnings("unused")
-    private void dismissAllTasks(View view) {
+    public void dismissAllTasks(View view) {
         runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
         mContainer.getStatsLogManager().logger().log(LAUNCHER_TASK_CLEAR_ALL);
     }
diff --git a/res/drawable/ic_close.xml b/res/drawable/ic_close.xml
new file mode 100644
index 0000000..70565f2
--- /dev/null
+++ b/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M18.3,5.71a0.996,0.996 0,0 0,-1.41 0L12,10.59 7.11,5.7A0.996,0.996 0,1 0,5.7 7.11L10.59,12 5.7,16.89a0.996,0.996 0,1 0,1.41 1.41L12,13.41l4.89,4.89a0.996,0.996 0,1 0,1.41 -1.41L13.41,12l4.89,-4.89c0.38,-0.38 0.38,-1.02 0,-1.4z"
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/res/drawable/ic_close_all.xml b/res/drawable/ic_close_all.xml
new file mode 100644
index 0000000..edccc22
--- /dev/null
+++ b/res/drawable/ic_close_all.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M5,13H19V11H5M3,17H17V15H3M7,7V9H21V7"
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/res/layout/search_container_hotseat.xml b/res/layout/search_container_hotseat.xml
index 8f12ca0..3cbd402 100644
--- a/res/layout/search_container_hotseat.xml
+++ b/res/layout/search_container_hotseat.xml
@@ -14,7 +14,22 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<View
+<!--<View
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="0dp" />
\ No newline at end of file
+    android:layout_height="0dp" />-->
+
+<com.android.launcher3.qsb.QsbContainerView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:id="@id/search_container_hotseat"
+        android:padding="0dp" >
+
+    <fragment
+        android:name="com.android.launcher3.qsb.QsbContainerView$QsbFragment"
+        android:layout_width="match_parent"
+        android:tag="qsb_view_hotseat"
+        android:layout_height="match_parent"/>
+</com.android.launcher3.qsb.QsbContainerView>
\ No newline at end of file
diff --git a/res/layout/search_container_hotseat_empty.xml b/res/layout/search_container_hotseat_empty.xml
new file mode 100644
index 0000000..8f12ca0
--- /dev/null
+++ b/res/layout/search_container_hotseat_empty.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 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.
+-->
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/layout/search_container_hotseat_taskbar.xml b/res/layout/search_container_hotseat_taskbar.xml
new file mode 100644
index 0000000..8f12ca0
--- /dev/null
+++ b/res/layout/search_container_hotseat_taskbar.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 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.
+-->
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/layout/search_container_workspace.xml b/res/layout/search_container_workspace.xml
index 1c617b1..3cf4f74 100644
--- a/res/layout/search_container_workspace.xml
+++ b/res/layout/search_container_workspace.xml
@@ -25,6 +25,6 @@
     <fragment
         android:name="com.android.launcher3.qsb.QsbContainerView$QsbFragment"
         android:layout_width="match_parent"
-        android:tag="qsb_view"
+        android:tag="qsb_view_workspace"
         android:layout_height="match_parent"/>
 </com.android.launcher3.qsb.QsbContainerView>
\ No newline at end of file
diff --git a/res/values-v31/styles.xml b/res/values-v31/styles.xml
index 932ce38..0069426 100644
--- a/res/values-v31/styles.xml
+++ b/res/values-v31/styles.xml
@@ -25,10 +25,20 @@
         <item name="android:navigationBarColor">@android:color/transparent</item>
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:switchStyle">@style/SwitchStyle</item>
-        <item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
+        <item name="android:textAppearanceListItem">@style/Omni.PreferenceTitle</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
-        <item name="preferenceTheme">@style/HomeSettings.PreferenceTheme</item>
+        <item name="preferenceTheme">@style/Omni.PreferenceTheme</item>
+        <item name="alertDialogTheme">@style/AlertDialogTheme</item>
+    </style>
+
+    <style name="AlertDialogTheme" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert">
+        <item name="colorAccent">@color/home_settings_header_accent</item>
+        <item name="android:colorBackground">@color/home_settings_header_expanded</item>
+        <item name="android:windowSoftInputMode">adjustResize</item>
+        <item name="android:clipToPadding">true</item>
+        <item name="android:clipChildren">true</item>
+        <item name="dialogCornerRadius">8dp</item>
     </style>
 
     <style name="HomeSettings.PreferenceTheme" parent="@style/PreferenceThemeOverlay">
diff --git a/res/values/custom_arrays.xml b/res/values/custom_arrays.xml
new file mode 100644
index 0000000..37a0a19
--- /dev/null
+++ b/res/values/custom_arrays.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  Copyright (C) 2018 The OmniROM Project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array name="prefs_qsb_location_entries">
+        <item>@string/pref_qsb_location_hotseat</item>
+        <item>@string/pref_qsb_location_workspace</item>
+    </string-array>
+    <string-array name="prefs_qsb_location_values">
+        <item>"0"</item>
+        <item>"1"</item>
+    </string-array>
+</resources>
diff --git a/res/values/custom_config.xml b/res/values/custom_config.xml
new file mode 100644
index 0000000..d771248
--- /dev/null
+++ b/res/values/custom_config.xml
@@ -0,0 +1,4 @@
+<resources>
+    <!-- Miscellaneous -->
+    <bool name="qsb_show_default">true</bool>
+</resources>
diff --git a/res/values/custom_dimens.xml b/res/values/custom_dimens.xml
new file mode 100644
index 0000000..ddf8791
--- /dev/null
+++ b/res/values/custom_dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  Copyright (C) 2018 The OmniROM Project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <dimen name="hotseat_qsb_margin_top">8dp</dimen>
+
+</resources>
diff --git a/res/values/custom_strings.xml b/res/values/custom_strings.xml
new file mode 100644
index 0000000..a063d2f
--- /dev/null
+++ b/res/values/custom_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  Copyright (C) 2018 The OmniROM Project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="state_loading">Applying\u2026</string>
+    <string name="pref_qsb_show_summary"></string>
+    <string name="pref_qsb_show_title">Show search widget</string>
+    <string name="pref_qsb_location_title">Search widget location</string>
+    <string name="pref_qsb_location_hotseat">Hotseat</string>
+    <string name="pref_qsb_location_hotseat_value" translatable="false">0</string>
+    <string name="pref_qsb_location_workspace">Workspace</string>
+    <string name="pref_qsb_location_workspace_value" translatable="false">1</string>
+    <string name="pref_grid_title">Grid size</string>
+    <string name="left_tab_label">Google feed tab</string>
+    <string name="left_tab_description">Toggle Google feed intergration</string>
+
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 21aabfa..b2f7353 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -115,7 +115,7 @@
     <dimen name="all_apps_header_pill_corner_radius">12dp</dimen>
     <dimen name="all_apps_header_tab_height">48dp</dimen>
     <dimen name="all_apps_tabs_indicator_height">2dp</dimen>
-    <dimen name="all_apps_header_top_margin">33dp</dimen>
+    <dimen name="all_apps_header_top_margin">50dp</dimen>
     <dimen name="all_apps_header_top_padding">36dp</dimen>
     <!-- Additional top padding to add when Floating Searchbar is enabled. -->
     <dimen name="all_apps_additional_top_padding_floating_search">16dp</dimen>
@@ -431,7 +431,7 @@
         @*android:dimen/rounded_corner_content_padding
     </dimen>
     <dimen name="taskbar_stashed_size">0dp</dimen>
-    <dimen name="qsb_widget_height">0dp</dimen>
+    <dimen name="qsb_widget_height">72dp</dimen>
     <dimen name="qsb_shadow_height">0dp</dimen>
     <dimen name="min_hotseat_icon_space">18dp</dimen>
     <dimen name="max_hotseat_icon_space">50dp</dimen>
diff --git a/res/xml/default_workspace_3x3.xml b/res/xml/default_workspace_3x3.xml
index 31376e1..5d4e8b9 100644
--- a/res/xml/default_workspace_3x3.xml
+++ b/res/xml/default_workspace_3x3.xml
@@ -17,7 +17,7 @@
 <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, [All Apps], Dialer -->
+    <!-- Dialer, Messaging, Settings  -->
 
     <resolve
         launcher:container="-101"
@@ -48,24 +48,7 @@
         launcher:screen="0"
         launcher:x="0"
         launcher:y="-1" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
-        <favorite launcher:uri="mailto:" />
-    </resolve>
-
-    <resolve
-        launcher:screen="0"
-        launcher:x="1"
-        launcher:y="-1" >
-        <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:screen="0"
-        launcher:x="2"
-        launcher:y="-1" >
-        <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" />
+        <favorite launcher:uri="#Intent;action=org.omnirom.control.intents.LAUNCH;category=android.intent.category.DEFAULT;end" />
     </resolve>
 
 </favorites>
diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml
index bf3c62c..84b2828 100644
--- a/res/xml/default_workspace_4x4.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -17,7 +17,7 @@
 <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, Browser, Camera -->
+    <!-- Dialer, Messaging, Settings, Camera -->
     <resolve
         launcher:container="-101"
         launcher:screen="0"
@@ -59,29 +59,12 @@
         <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
     </resolve>
 
-    <!-- Bottom row -->
+    <!-- Bottom row OmniControl -->
     <resolve
         launcher:screen="0"
         launcher:x="0"
         launcher:y="-1" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
-        <favorite launcher:uri="mailto:" />
-    </resolve>
-
-    <resolve
-        launcher:screen="0"
-        launcher:x="1"
-        launcher:y="-1" >
-        <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:screen="0"
-        launcher:x="3"
-        launcher:y="-1" >
-        <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" />
+        <favorite launcher:uri="#Intent;action=org.omnirom.control.intents.LAUNCH;category=android.intent.category.DEFAULT;end" />
     </resolve>
 
 </favorites>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index b4ac8f6..e74d7f1 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -17,7 +17,7 @@
 <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, [Maps/Music], Browser, Camera -->
+    <!-- Dialer, Messaging, Settings, OmniControl, Camera -->
     <resolve
         launcher:container="-101"
         launcher:screen="0"
@@ -45,8 +45,7 @@
         launcher:screen="2"
         launcher:x="2"
         launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;end" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
+        <favorite launcher:uri="#Intent;action=android.settings.SETTINGS;end" />
     </resolve>
 
     <resolve
@@ -54,9 +53,7 @@
         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/" />
+        <favorite launcher:uri="#Intent;action=org.omnirom.control.intents.LAUNCH;category=android.intent.category.DEFAULT;end" />
     </resolve>
 
     <resolve
@@ -68,31 +65,4 @@
         <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
     </resolve>
 
-    <!-- Bottom row -->
-    <resolve
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="-1" >
-	    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
-	    <favorite launcher:uri="mailto:" />
-
-    </resolve>
-
-    <resolve
-        launcher:screen="0"
-        launcher:x="1"
-        launcher:y="-1" >
-	    <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:screen="0"
-        launcher:x="4"
-        launcher:y="-1" >
-        <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_6x6.xml b/res/xml/default_workspace_6x6.xml
new file mode 100644
index 0000000..63a64fc
--- /dev/null
+++ b/res/xml/default_workspace_6x6.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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, nothing, Settings, OmniControl, 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>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.settings.SETTINGS;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=org.omnirom.control.intents.LAUNCH;category=android.intent.category.DEFAULT;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="5"
+        launcher:x="5"
+        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/device_profiles.xml b/res/xml/device_profiles.xml
index 1d0dbff..8366aea 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -161,6 +161,50 @@
     </grid-option>
 
     <grid-option
+        launcher:name="6_by_6"
+        launcher:numRows="6"
+        launcher:numColumns="6"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="4"
+        launcher:numHotseatIcons="6"
+        launcher:dbFile="launcher.db"
+        launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_split"
+        launcher:defaultLayoutId="@xml/default_workspace_6x6"
+        launcher:deviceCategory="phone|multi_display" >
+
+        <display-option
+            launcher:name="Large Phone"
+            launcher:minWidthDps="406"
+            launcher:minHeightDps="694"
+            launcher:iconImageSize="56"
+            launcher:iconTextSize="14.4"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Large Phone Split Display"
+            launcher:minWidthDps="406"
+            launcher:minHeightDps="694"
+            launcher:iconImageSize="56"
+            launcher:iconTextSize="14.4"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
+            launcher:canBeDefault="true" />
+
+        <display-option
+            launcher:name="Shorter Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="400"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:allAppsBorderSpace="16"
+            launcher:allAppsCellHeight="104"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
+    <grid-option
         launcher:name="6_by_5"
         launcher:numRows="5"
         launcher:numColumns="6"
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 284ab9e..6c161fe 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -50,4 +50,31 @@
         launcher:logIdOn="615"
         launcher:logIdOff="616" />
 
+    <SwitchPreference
+        android:defaultValue="@bool/qsb_show_default"
+        android:key="pref_qsb_show"
+        android:persistent="true"
+        android:summary="@string/pref_qsb_show_summary"
+        android:title="@string/pref_qsb_show_title"/>
+
+    <ListPreference
+        android:key="pref_qsb_location"
+        android:title="@string/pref_qsb_location_title"
+        android:persistent="true"
+        android:dependency="pref_qsb_show"
+        android:entries="@array/prefs_qsb_location_entries"
+        android:entryValues="@array/prefs_qsb_location_values"
+        android:defaultValue="@string/pref_qsb_location_workspace_value"/>
+
+    <SwitchPreference
+        android:key="pref_left_tab"
+        android:title="@string/left_tab_label"
+        android:summary="@string/left_tab_description"
+        android:persistent="true" />
+
+    <ListPreference
+        android:key="pref_grid"
+        android:title="@string/pref_grid_title"
+        android:persistent="false"/>
+
 </androidx.preference.PreferenceScreen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index f1274dc..f8b21f0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -73,6 +73,7 @@
 import com.android.launcher3.util.ResourceHelper;
 import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.util.window.WindowManagerProxy;
+import com.android.launcher3.Utilities;
 
 import java.io.PrintWriter;
 import java.util.Locale;
@@ -235,6 +236,7 @@
     public final int hotseatQsbHeight;
     public final int hotseatQsbVisualHeight;
     private final int hotseatQsbShadowHeight;
+    private final int hotseatQsbMarginTop;
     public int hotseatBorderSpace;
     private final int mMinHotseatIconSpacePx;
     private final int mMinHotseatQsbWidthPx;
@@ -366,6 +368,7 @@
         hotseatQsbHeight = 0;
         hotseatQsbVisualHeight = 0;
         hotseatQsbShadowHeight = 0;
+        hotseatQsbMarginTop = 0;
         hotseatBorderSpace = 0;
         mMinHotseatIconSpacePx = 0;
         mMinHotseatQsbWidthPx = 0;
@@ -610,9 +613,10 @@
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
-        hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
+        hotseatQsbHeight = Utilities.showHotseatQsbWidget(context) ? res.getDimensionPixelSize(R.dimen.qsb_widget_height) : 0;
         hotseatQsbShadowHeight = res.getDimensionPixelSize(R.dimen.qsb_shadow_height);
         hotseatQsbVisualHeight = hotseatQsbHeight - 2 * hotseatQsbShadowHeight;
+        hotseatQsbMarginTop = res.getDimensionPixelSize(R.dimen.hotseat_qsb_margin_top);
 
         // Whether QSB might be inline in appropriate orientation (e.g. landscape).
         boolean canQsbInline = (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
@@ -970,6 +974,13 @@
     }
 
     /**
+     * used in Hotseat only for !isQsbInline
+     */
+    public int getHostseatQsbWidth() {
+        return calculateQsbWidth(0);
+    }
+
+    /**
      * Calculates the width of the hotseat, changing spaces between the icons and removing icons if
      * necessary.
      */
@@ -2053,7 +2064,7 @@
         } else if (isTaskbarPresent) { // QSB on top
             return hotseatBarSizePx - hotseatQsbHeight + hotseatQsbShadowHeight;
         } else {
-            return hotseatBarBottomSpacePx - hotseatQsbShadowHeight;
+            return hotseatBarBottomSpacePx - hotseatQsbShadowHeight - hotseatQsbMarginTop;
         }
     }
 
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b20d8a5..a83db06 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -33,6 +33,8 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.Utilities;
+
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
@@ -99,7 +101,12 @@
 
     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+
+        if (Utilities.showHotseatQsbWidget(context)) {
+            mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+        } else {
+            mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat_empty, this, false);
+        }
         addView(mQsb);
         mIconsAlphaChannels = new MultiValueAlpha(getShortcutsAndWidgets(),
                 ALPHA_CHANNEL_CHANNELS_COUNT);
@@ -299,7 +306,8 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
         DeviceProfile dp = mActivity.getDeviceProfile();
-        mQsb.measure(MeasureSpec.makeMeasureSpec(dp.hotseatQsbWidth, MeasureSpec.EXACTLY),
+        int hotseatQsbWidth = dp.isQsbInline ? dp.hotseatQsbWidth : dp.getHostseatQsbWidth();
+        mQsb.measure(MeasureSpec.makeMeasureSpec(hotseatQsbWidth, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(dp.hotseatQsbHeight, MeasureSpec.EXACTLY));
     }
 
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index 5d32525..008d229 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -40,7 +40,7 @@
     var pagesToBindSynchronously = LIntSet()
 
     private var isFirstPagePinnedItemEnabled =
-        (BuildConfig.QSB_ON_FIRST_SCREEN && !enableSmartspaceRemovalToggle())
+        (FeatureFlags.QSB_ON_FIRST_SCREEN && !enableSmartspaceRemovalToggle())
 
     var stringCache: StringCache? = null
 
@@ -332,13 +332,13 @@
         )
         val firstScreenPosition = 0
         if (
-            (isFirstPagePinnedItemEnabled && !SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
+            (getQsbOnFirstScreen() && isFirstPagePinnedItemEnabled && !SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
                 orderedScreenIds.indexOf(FIRST_SCREEN_ID) != firstScreenPosition
         ) {
             orderedScreenIds.removeValue(FIRST_SCREEN_ID)
             orderedScreenIds.add(firstScreenPosition, FIRST_SCREEN_ID)
         } else if (
-            (!isFirstPagePinnedItemEnabled || SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
+            (!getQsbOnFirstScreen() && !isFirstPagePinnedItemEnabled || SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
                 orderedScreenIds.isEmpty
         ) {
             // If there are no screens, we need to have an empty screen
@@ -395,6 +395,7 @@
         }
         orderedScreenIds
             .filterNot { screenId ->
+                getQsbOnFirstScreen() &&
                 isFirstPagePinnedItemEnabled &&
                     !SHOULD_SHOW_FIRST_PAGE_WIDGET &&
                     screenId == WorkspaceLayoutManager.FIRST_SCREEN_ID
@@ -432,6 +433,8 @@
         launcher.appsView.updateWorkUI()
     }
 
+    fun getQsbOnFirstScreen(): Boolean = Utilities.showWorkspaceQsbWidget(launcher)
+
     fun getIsFirstPagePinnedItemEnabled(): Boolean = isFirstPagePinnedItemEnabled
 
     override fun getItemInflater() = launcher.itemInflater
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ebcb5da..aa4ad12 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -22,11 +22,13 @@
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
+import static com.android.launcher3.settings.SettingsActivity.QSB_LOCATION_PREFERENCE_KEY;
 
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.Person;
+import android.app.ProgressDialog;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.pm.LauncherActivityInfo;
@@ -87,6 +89,7 @@
 import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
@@ -167,6 +170,9 @@
         sIsRunningInTestHarness = true;
     }
 
+    public static final String QSB_SHOW = "pref_qsb_show";
+    public static final long WAIT_BEFORE_RESTART = 250;
+
     /** Disables running test in test harness mode */
     public static void disableRunningInTestHarnessForTests() {
         sIsRunningInTestHarness = false;
@@ -176,6 +182,24 @@
         return Log.isLoggable(propertyName, Log.VERBOSE);
     }
 
+    public static boolean showQsbWidget(Context context) {
+         return LauncherPrefs.getPrefs(context).getBoolean(QSB_SHOW,
+                context.getResources().getBoolean(R.bool.qsb_show_default));
+    }
+
+    public static String qsbWidgtLocation(Context context) {
+         return LauncherPrefs.getPrefs(context).getString(QSB_LOCATION_PREFERENCE_KEY,
+                context.getResources().getString(R.string.pref_qsb_location_workspace_value));
+    }
+
+    public static boolean showHotseatQsbWidget(Context context) {
+         return showQsbWidget(context) && qsbWidgtLocation(context).equals(context.getResources().getString(R.string.pref_qsb_location_hotseat_value));
+    }
+
+    public static boolean showWorkspaceQsbWidget(Context context) {
+         return showQsbWidget(context) && qsbWidgtLocation(context).equals(context.getResources().getString(R.string.pref_qsb_location_workspace_value));
+    }
+
     /**
      * Given a coordinate relative to the descendant, find the coordinate in a parent view's
      * coordinates.
@@ -938,4 +962,15 @@
         }
         return null;
     }
+
+    public static void restart(final Context context) {
+        ProgressDialog.show(context, null, context.getString(R.string.state_loading), true, false);
+        Executors.MODEL_EXECUTOR.execute(() -> {
+            try {
+                Thread.sleep(WAIT_BEFORE_RESTART);
+            } catch (Exception ignored) {
+            }
+            android.os.Process.killProcess(android.os.Process.myPid());
+        });
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 69a5a83..4f7ee07 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -606,7 +606,7 @@
      * Initializes and binds the first page
      */
     public void bindAndInitFirstWorkspaceScreen() {
-        if ((!FeatureFlags.QSB_ON_FIRST_SCREEN
+        if ((!Utilities.showWorkspaceQsbWidget(mContext)
                 || !mLauncher.getIsFirstPagePinnedItemEnabled())
                 || SHOULD_SHOW_FIRST_PAGE_WIDGET) {
             mFirstPagePinnedItem = null;
@@ -1047,7 +1047,7 @@
             int id = mWorkspaceScreens.keyAt(i);
             CellLayout cl = mWorkspaceScreens.valueAt(i);
             // FIRST_SCREEN_ID can never be removed.
-            if (((!FeatureFlags.QSB_ON_FIRST_SCREEN
+            if (((!Utilities.showWorkspaceQsbWidget(getContext())
                     || SHOULD_SHOW_FIRST_PAGE_WIDGET)
                     || id > FIRST_SCREEN_ID)
                     && cl.getShortcutsAndWidgets().getChildCount() == 0) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 9e38824..a27b2ac 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -50,6 +50,7 @@
      * @deprecated Use {@link BuildConfig#QSB_ON_FIRST_SCREEN} directly
      */
     @Deprecated
+    // NOTE: replaced by resource qsb_show_default
     public static final boolean QSB_ON_FIRST_SCREEN = BuildConfig.QSB_ON_FIRST_SCREEN;
 
     /**
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index f0e4fc4..0aa4cf2 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -69,6 +69,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.WorkspaceLayoutManager;
 import com.android.launcher3.apppairs.AppPairIcon;
@@ -522,7 +523,7 @@
         }
 
         // Add first page QSB
-        if (FeatureFlags.QSB_ON_FIRST_SCREEN && dataModel.isFirstPagePinnedItemEnabled
+        if (Utilities.showWorkspaceQsbWidget(mContext) && dataModel.isFirstPagePinnedItemEnabled
                 && !SHOULD_SHOW_FIRST_PAGE_WIDGET) {
             CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
             View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false);
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index b9b1e98..fc1355d 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -162,9 +162,7 @@
                 screenSet.add(item.screenId);
             }
         }
-        if ((FeatureFlags.QSB_ON_FIRST_SCREEN
-                && !SHOULD_SHOW_FIRST_PAGE_WIDGET)
-                || screenSet.isEmpty()) {
+        if (screenSet.isEmpty()) {
             screenSet.add(Workspace.FIRST_SCREEN_ID);
         }
         return screenSet.getArray();
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index ed4f492..9e9f918 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -257,7 +257,7 @@
                         Favorites.SCREEN, IntArray.wrap(-777, -778)), null);
             }
             case 30: {
-                if (FeatureFlags.QSB_ON_FIRST_SCREEN
+                if (Utilities.showWorkspaceQsbWidget(mContext)
                         && !SHOULD_SHOW_FIRST_PAGE_WIDGET) {
                     // Clean up first row in screen 0 as it might contain junk data.
                     Log.d(TAG, "Cleaning up first row");
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index bfa00bd..80ac0cb 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -360,7 +360,7 @@
         final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
         final Point trg = new Point(trgX, trgY);
         final Point next = new Point(0, screenId == 0
-                && (FeatureFlags.QSB_ON_FIRST_SCREEN
+                && (Utilities.showWorkspaceQsbWidget(destReader.mContext)
                 && (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(destReader.mContext)
                 .getBoolean(SMARTSPACE_ON_HOME_SCREEN, true))
                 && !SHOULD_SHOW_FIRST_PAGE_WIDGET)
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index c01b1b6..742fc31 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -553,7 +553,7 @@
 
         if (!mOccupied.containsKey(item.screenId)) {
             GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
-            if (item.screenId == Workspace.FIRST_SCREEN_ID && (FeatureFlags.QSB_ON_FIRST_SCREEN
+            if (item.screenId == Workspace.FIRST_SCREEN_ID && (Utilities.showWorkspaceQsbWidget(mContext)
                     && !SHOULD_SHOW_FIRST_PAGE_WIDGET
                     && isFirstPagePinnedItemEnabled)) {
                 // Mark the first X columns (X is width of the search container) in the first row as
@@ -561,7 +561,7 @@
                 // container.
                 int spanX = mIDP.numSearchContainerColumns;
                 int spanY = 1;
-                screen.markCells(0, 0, spanX, spanY, true);
+                screen.markCells(0, 0, spanX, spanY, Utilities.showWorkspaceQsbWidget(mContext));
             }
             mOccupied.put(item.screenId, screen);
         }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f96e959..c70c2db 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -417,7 +417,7 @@
         }
         logASplit("loadWorkspace finished");
 
-        mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
+        mBgDataModel.isFirstPagePinnedItemEnabled = Utilities.showQsbWidget(mApp.getContext())
                 && (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
                 mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
     }
diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
index 1a6d178..7e025ac 100644
--- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
+++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
@@ -67,7 +68,7 @@
         int screenCount = workspaceScreens.size();
         // First check the preferred screen.
         IntSet screensToExclude = new IntSet();
-        if (FeatureFlags.QSB_ON_FIRST_SCREEN
+        if (Utilities.showWorkspaceQsbWidget(app.getContext())
                 && !SHOULD_SHOW_FIRST_PAGE_WIDGET) {
             screensToExclude.add(FIRST_SCREEN_ID);
         }
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index d6b41b0..ac20d13 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.FragmentWithPreview;
@@ -280,19 +281,14 @@
         }
 
         private void rebindFragment() {
-            // Exit if the embedded qsb is disabled
-            if (!isQsbEnabled()) {
-                return;
-            }
-
-            if (mWrapper != null && getContext() != null) {
+            if (mWrapper != null && getContext() != null && isQsbEnabled()) {
                 mWrapper.removeAllViews();
                 mWrapper.addView(createQsb(mWrapper));
             }
         }
 
-        public boolean isQsbEnabled() {
-            return FeatureFlags.QSB_ON_FIRST_SCREEN
+        private boolean isQsbEnabled() {
+            return Utilities.showQsbWidget(getContext())
                     && !SHOULD_SHOW_FIRST_PAGE_WIDGET;
         }
 
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 6008287..6a0527c 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -30,9 +30,13 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.net.Uri;
+import android.content.res.XmlResourceParser;
 import android.os.Bundle;
+import android.os.Handler;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Log;
+import android.util.Xml;
 import android.view.MenuItem;
 import android.view.View;
 
@@ -43,6 +47,7 @@
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 import androidx.fragment.app.FragmentManager;
+import androidx.preference.ListPreference;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
@@ -55,17 +60,29 @@
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.GridOption;
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.R;
 import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SettingsCache;
 
+import org.omnirom.omnilib.utils.PackageUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
  */
 public class SettingsActivity extends FragmentActivity
         implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback {
+    private static final String TAG = "SettingsActivity";
 
     @VisibleForTesting
     static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
@@ -73,6 +90,8 @@
     public static final String FIXED_LANDSCAPE_MODE = "pref_fixed_landscape_mode";
 
     private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
+    private static final String GRID_SIZE_PREFERENCE_KEY = "pref_grid";
+    public static final String QSB_LOCATION_PREFERENCE_KEY = "pref_qsb_location";
 
     public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
 
@@ -84,6 +103,9 @@
     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
     public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
+    private static final String SEARCH_PACKAGE = "com.google.android.googlequicksearchbox";
+    private static final String SHOW_LEFT_TAB_PREFERENCE_KEY = "pref_left_tab";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -227,6 +249,77 @@
             if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
                 getActivity().setTitle(getPreferenceScreen().getTitle());
             }
+
+            Preference showQsbWidget = findPreference(Utilities.QSB_SHOW);
+            showQsbWidget.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    new Handler().postDelayed(() -> Utilities.restart(getActivity()), Utilities.WAIT_BEFORE_RESTART);
+                    return true;
+                }
+            });
+
+            Preference leftTabPage = findPreference(SHOW_LEFT_TAB_PREFERENCE_KEY);
+            if (!isSearchInstalled()) {
+                getPreferenceScreen().removePreference(leftTabPage);
+            }
+            leftTabPage.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    new Handler().postDelayed(() -> Utilities.restart(getActivity()), Utilities.WAIT_BEFORE_RESTART);
+                    return true;
+                }
+            });
+
+            final ListPreference grid = (ListPreference) findPreference(GRID_SIZE_PREFERENCE_KEY);
+            InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
+            ArrayList<String> entries = new ArrayList<>();
+            ArrayList<String> values = new ArrayList<>();
+            for (GridOption gridOption : parseAllGridOptions(idp.deviceType)) {
+                values.add(gridOption.name);
+                entries.add(gridOption.numColumns + " x " + gridOption.numRows);
+            }
+
+            grid.setEntries(entries.toArray(new String[entries.size()]));
+            grid.setEntryValues(values.toArray(new String[values.size()]));
+
+            String currentGrid = idp.getCurrentGridName(getContext());
+            int valueIndex = grid.findIndexOfValue(currentGrid);
+            grid.setValueIndex(valueIndex >= 0 ? valueIndex : 0);
+            grid.setSummary(grid.getEntry());
+
+            grid.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    // Verify that this is a valid grid option
+                    String gridName = (String) newValue;
+                    GridOption match = null;
+                    for (GridOption option : parseAllGridOptions(idp.deviceType)) {
+                        if (option.name.equals(gridName)) {
+                            match = option;
+                            break;
+                        }
+                    }
+                    if (match == null) {
+                        return false;
+                    }
+
+                    InvariantDeviceProfile.INSTANCE.get(getContext())
+                            .setCurrentGrid(getContext(), gridName);
+
+                    int valueIndex = grid.findIndexOfValue(gridName);
+                    grid.setValueIndex(valueIndex >= 0 ? valueIndex : 0);
+                    grid.setSummary(grid.getEntries()[valueIndex]);
+                    return true;
+                }
+            });
+
+            final ListPreference qsbLocation = (ListPreference) findPreference(QSB_LOCATION_PREFERENCE_KEY);
+            valueIndex = qsbLocation.findIndexOfValue(qsbLocation.getValue());
+            qsbLocation.setSummary(qsbLocation.getEntries()[valueIndex]);
+            qsbLocation.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    new Handler().postDelayed(() -> Utilities.restart(getActivity()), Utilities.WAIT_BEFORE_RESTART);
+                    return true;
+                }
+            });
         }
 
         private boolean isKeyInPreferenceGroup(String targetKey, PreferenceGroup parent) {
@@ -405,5 +498,31 @@
                     list, position, screen.findPreference(mHighLightKey))
                     : null;
         }
+
+        private List<GridOption> parseAllGridOptions(int deviceType) {
+            List<GridOption> result = new ArrayList<>();
+            try (XmlResourceParser parser = getContext().getResources().getXml(R.xml.device_profiles)) {
+                final int depth = parser.getDepth();
+                int type;
+                while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                        parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                    if ((type == XmlPullParser.START_TAG)
+                            && GridOption.TAG_NAME.equals(parser.getName())) {
+                        GridOption gridOption = new GridOption(getContext(), Xml.asAttributeSet(parser));
+                        if (gridOption.isEnabled(deviceType)) {
+                            result.add(gridOption);
+                        }
+                    }
+                }
+            } catch (IOException | XmlPullParserException e) {
+                Log.e(TAG, "Error parsing device profile", e);
+                return Collections.emptyList();
+            }
+            return result;
+        }
+
+        private boolean isSearchInstalled() {
+            return PackageUtils.isAvailableApp(SEARCH_PACKAGE, getActivity());
+        }
     }
 }
diff --git a/src_overlay/google/com/android/launcher3/overlay/OverlayCallbackImpl.java b/src_overlay/google/com/android/launcher3/overlay/OverlayCallbackImpl.java
new file mode 100644
index 0000000..d068cf5
--- /dev/null
+++ b/src_overlay/google/com/android/launcher3/overlay/OverlayCallbackImpl.java
@@ -0,0 +1,171 @@
+/*
+ * 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.overlay;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherPrefs;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
+
+import com.google.android.libraries.gsa.launcherclient.LauncherClient;
+import com.google.android.libraries.gsa.launcherclient.LauncherClientCallbacks;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link LauncherOverlay} and passes all the corresponding events to {@link
+ * LauncherClient}. {@see setClient}
+ *
+ * <p>Implements {@link LauncherClientCallbacks} and sends all the corresponding callbacks to {@link
+ * Launcher}.
+ */
+public class OverlayCallbackImpl
+        implements LauncherOverlay, LauncherClientCallbacks, LauncherOverlayManager,
+        SharedPreferences.OnSharedPreferenceChangeListener {
+
+    private static final String SHOW_LEFT_TAB_PREFERENCE_KEY = "pref_left_tab";
+
+    private final Launcher mLauncher;
+    private final LauncherClient mClient;
+
+    private LauncherOverlayCallbacks mLauncherOverlayCallbacks;
+    private boolean mWasOverlayAttached = false;
+
+    public OverlayCallbackImpl(Launcher launcher) {
+        SharedPreferences prefs = LauncherPrefs.getPrefs(launcher);
+
+        mLauncher = launcher;
+        mClient = new LauncherClient(mLauncher, this, getClientOptions(prefs));
+        prefs.registerOnSharedPreferenceChangeListener(this);
+    }
+
+    @Override
+    public void onDeviceProvideChanged() {
+        mClient.reattachOverlay();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        mClient.onAttachedToWindow();
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        mClient.onDetachedFromWindow();
+    }
+
+    @Override
+    public void dump(String prefix, PrintWriter w) {
+        mClient.dump(prefix, w);
+    }
+
+    @Override
+    public void openOverlay() {
+        mClient.showOverlay(true);
+    }
+
+    @Override
+    public void hideOverlay(boolean animate) {
+        mClient.hideOverlay(animate);
+    }
+
+    @Override
+    public void hideOverlay(int duration) {
+        mClient.hideOverlay(duration);
+    }
+
+    @Override
+    public void onActivityStarted() {
+        mClient.onStart();
+    }
+
+    @Override
+    public void onActivityResumed() {
+        mClient.onResume();
+    }
+
+    @Override
+    public void onActivityPaused() {
+        mClient.onPause();
+    }
+
+    @Override
+    public void onActivityStopped() {
+        mClient.onStop();
+    }
+
+    @Override
+    public void onActivityDestroyed() {
+        mClient.onDestroy();
+        mLauncher.getSharedPrefs().unregisterOnSharedPreferenceChangeListener(this);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        if (SHOW_LEFT_TAB_PREFERENCE_KEY.equals(key)) {
+            mClient.setClientOptions(getClientOptions(prefs));
+        }
+    }
+
+    @Override
+    public void onServiceStateChanged(boolean overlayAttached, boolean hotwordActive) {
+        if (overlayAttached != mWasOverlayAttached) {
+            mWasOverlayAttached = overlayAttached;
+            mLauncher.setLauncherOverlay(overlayAttached ? this : null);
+        }
+    }
+
+    @Override
+    public void onOverlayScrollChanged(float progress) {
+        if (mLauncherOverlayCallbacks != null) {
+            mLauncherOverlayCallbacks.onOverlayScrollChanged(progress);
+        }
+    }
+
+    @Override
+    public void onScrollInteractionBegin() {
+        mClient.startMove();
+    }
+
+    @Override
+    public void onScrollInteractionEnd() {
+        mClient.endMove();
+    }
+
+    @Override
+    public void onScrollChange(float progress, boolean rtl) {
+        mClient.updateMove(progress);
+    }
+
+    @Override
+    public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks) {
+        mLauncherOverlayCallbacks = callbacks;
+    }
+
+    private LauncherClient.ClientOptions getClientOptions(SharedPreferences prefs) {
+        return new LauncherClient.ClientOptions(
+                prefs.getBoolean(SHOW_LEFT_TAB_PREFERENCE_KEY, false),
+                true, /* enableHotword */
+                true /* enablePrewarming */
+        );
+    }
+}
diff --git a/src_overlay/mock/com/android/launcher3/overlay/OverlayCallbackImpl.java b/src_overlay/mock/com/android/launcher3/overlay/OverlayCallbackImpl.java
new file mode 100644
index 0000000..449cef8
--- /dev/null
+++ b/src_overlay/mock/com/android/launcher3/overlay/OverlayCallbackImpl.java
@@ -0,0 +1,121 @@
+/*
+ * 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.overlay;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+import com.android.launcher3.Launcher;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
+
+import com.google.android.libraries.gsa.launcherclient.LauncherClientCallbacks;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link LauncherOverlay} and passes all the corresponding events to {@link
+ * LauncherClient}. {@see setClient}
+ *
+ * <p>Implements {@link LauncherClientCallbacks} and sends all the corresponding callbacks to {@link
+ * Launcher}.
+ */
+public class OverlayCallbackImpl
+        implements LauncherOverlay, LauncherClientCallbacks, LauncherOverlayManager,
+        SharedPreferences.OnSharedPreferenceChangeListener {
+
+    public OverlayCallbackImpl(Launcher launcher) {
+    }
+
+    @Override
+    public void onDeviceProvideChanged() {
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+    }
+
+    @Override
+    public void dump(String prefix, PrintWriter w) {
+    }
+
+    @Override
+    public void openOverlay() {
+    }
+
+    @Override
+    public void hideOverlay(boolean animate) {
+    }
+
+    @Override
+    public void hideOverlay(int duration) {
+    }
+
+    @Override
+    public void onActivityStarted() {
+    }
+
+    @Override
+    public void onActivityResumed() {
+    }
+
+    @Override
+    public void onActivityPaused() {
+    }
+
+    @Override
+    public void onActivityStopped() {
+    }
+
+    @Override
+    public void onActivityDestroyed() {
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+    }
+
+    @Override
+    public void onServiceStateChanged(boolean overlayAttached, boolean hotwordActive) {
+    }
+
+    @Override
+    public void onOverlayScrollChanged(float progress) {
+    }
+
+    @Override
+    public void onScrollInteractionBegin() {
+    }
+
+    @Override
+    public void onScrollInteractionEnd() {
+    }
+
+    @Override
+    public void onScrollChange(float progress, boolean rtl) {
+    }
+
+    @Override
+    public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks) {
+    }
+}
diff --git a/src_overlay/mock/com/google/android/libraries/gsa/launcherclient/LauncherClientCallbacks.java b/src_overlay/mock/com/google/android/libraries/gsa/launcherclient/LauncherClientCallbacks.java
new file mode 100644
index 0000000..3f9e6d1
--- /dev/null
+++ b/src_overlay/mock/com/google/android/libraries/gsa/launcherclient/LauncherClientCallbacks.java
@@ -0,0 +1,5 @@
+package com.google.android.libraries.gsa.launcherclient;
+public interface LauncherClientCallbacks {
+    void onOverlayScrollChanged(float progress);
+    void onServiceStateChanged(boolean overlayAttached, boolean hotwordActive);
+}
\ No newline at end of file