Merge "Fix flaky enter key"
diff --git a/Android.mk b/Android.mk
index 349a134..503f9ae 100644
--- a/Android.mk
+++ b/Android.mk
@@ -208,11 +208,13 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, go/src)
+    $(call all-java-files-under, go/src) \
+    $(call all-java-files-under, go/quickstep/src)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/quickstep/res \
-    $(LOCAL_PATH)/go/res
+    $(LOCAL_PATH)/go/res \
+    $(LOCAL_PATH)/go/quickstep/res
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := full
@@ -225,7 +227,7 @@
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
     $(LOCAL_PATH)/go/AndroidManifest.xml \
-    $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
+    $(LOCAL_PATH)/go/AndroidManifest-launcher.xml \
     $(LOCAL_PATH)/AndroidManifest-common.xml
 
 LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
diff --git a/go/AndroidManifest-launcher.xml b/go/AndroidManifest-launcher.xml
new file mode 100644
index 0000000..2223036
--- /dev/null
+++ b/go/AndroidManifest-launcher.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2020, 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.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3">
+    <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+    <!--
+    Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+    Refer comments around specific entries on how to extend individual components.
+    -->
+
+    <application
+        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:fullBackupOnly="true"
+        android:fullBackupContent="@xml/backupscheme"
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/ic_launcher_home"
+        android:label="@string/derived_app_name"
+        android:theme="@style/AppTheme"
+        android:largeHeap="@bool/config_largeHeap"
+        android:restoreAnyVersion="true"
+        android:supportsRtl="true" >
+
+        <!--
+        Main launcher activity. When extending only change the name, and keep all the
+        attributes and intent filters the same
+        -->
+        <activity
+            android:name="com.android.launcher3.Launcher3QuickStepGo"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:stateNotNeeded="true"
+            android:windowSoftInputMode="adjustPan"
+            android:screenOrientation="unspecified"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
+            android:resizeableActivity="true"
+            android:resumeWhilePausing="true"
+            android:taskAffinity=""
+            android:exported="true"
+            android:enabled="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.MONKEY"/>
+                <category android:name="android.intent.category.LAUNCHER_APP" />
+            </intent-filter>
+            <meta-data
+                android:name="com.android.launcher3.grid.control"
+                android:value="${packageName}.grid_control" />
+        </activity>
+
+    </application>
+</manifest>
diff --git a/go/quickstep/res/values/config.xml b/go/quickstep/res/values/config.xml
new file mode 100644
index 0000000..f376774
--- /dev/null
+++ b/go/quickstep/res/values/config.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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>
+    <string name="app_sharing_component" translatable="false"/>
+</resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/strings.xml b/go/quickstep/res/values/strings.xml
new file mode 100644
index 0000000..fdd8397
--- /dev/null
+++ b/go/quickstep/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Label for app share drop target. [CHAR_LIMIT=20] -->
+    <string name="app_share_drop_target_label">Share App</string>
+</resources>
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
new file mode 100644
index 0000000..b72e71c
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 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.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import androidx.core.content.FileProvider;
+
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.SystemShortcut;
+
+import java.io.File;
+
+/**
+ * Defines the Share system shortcut and its factory.
+ * This shortcut can be added to the app long-press menu on the home screen.
+ * Clicking the button will initiate peer-to-peer sharing of the app.
+ */
+public final class AppSharing {
+    /**
+     * This flag enables this feature. It is defined here rather than in launcher3's FeatureFlags
+     * because it is unique to Go and not toggleable at runtime.
+     */
+    public static final boolean ENABLE_APP_SHARING = true;
+
+    private static final String TAG = "AppSharing";
+    private static final String FILE_PROVIDER_SUFFIX = ".overview.fileprovider";
+    private static final String APP_EXSTENSION = ".apk";
+    private static final String APP_MIME_TYPE = "application/application";
+
+    private final String mSharingComponent;
+
+    private AppSharing(Launcher launcher) {
+        mSharingComponent = launcher.getText(R.string.app_sharing_component).toString();
+    }
+
+    private boolean canShare(ItemInfo info) {
+        /**
+         * TODO: Implement once b/168831749 has been resolved
+         * The implementation should check the validity of the app.
+         * It should also check whether the app is free or paid, returning false in the latter case.
+         * For now, all checks occur in the sharing app.
+         * So, we simply check whether the sharing app is defined.
+         */
+        return !TextUtils.isEmpty(mSharingComponent);
+    }
+
+    private Uri getShareableUri(Context context, String path, String displayName) {
+        String authority = BuildConfig.APPLICATION_ID + FILE_PROVIDER_SUFFIX;
+        File pathFile = new File(path);
+        return FileProvider.getUriForFile(context, authority, pathFile, displayName);
+    }
+
+    private SystemShortcut<Launcher> getShortcut(Launcher launcher, ItemInfo info) {
+        if (!canShare(info)) {
+            return null;
+        }
+
+        return new Share(launcher, info);
+    }
+
+    /**
+     * The Share App system shortcut, used to initiate p2p sharing of a given app
+     */
+    public final class Share extends SystemShortcut<Launcher> {
+        public Share(Launcher target, ItemInfo itemInfo) {
+            super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
+        }
+
+        @Override
+        public void onClick(View view) {
+            Intent sendIntent = new Intent();
+            sendIntent.setAction(Intent.ACTION_SEND);
+
+            ComponentName targetComponent = mItemInfo.getTargetComponent();
+            if (targetComponent == null) {
+                Log.e(TAG, "Item missing target component");
+                return;
+            }
+            String packageName = targetComponent.getPackageName();
+            PackageManager packageManager = view.getContext().getPackageManager();
+            String sourceDir, appLabel;
+            try {
+                PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
+                sourceDir = packageInfo.applicationInfo.sourceDir;
+                appLabel = packageManager.getApplicationLabel(packageInfo.applicationInfo)
+                        .toString() + APP_EXSTENSION;
+            } catch (Exception e) {
+                Log.e(TAG, "Could not find info for package \"" + packageName + "\"");
+                return;
+            }
+            Uri uri = getShareableUri(view.getContext(), sourceDir, appLabel);
+            sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
+            sendIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+
+            sendIntent.setType(APP_MIME_TYPE);
+            sendIntent.setComponent(ComponentName.unflattenFromString(mSharingComponent));
+
+            mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+
+            AbstractFloatingView.closeAllOpenViews(mTarget);
+        }
+    }
+
+    /**
+     * Shortcut factory for generating the Share App button
+     */
+    public static final SystemShortcut.Factory<Launcher> SHORTCUT_FACTORY = (launcher, itemInfo) ->
+            (new AppSharing(launcher)).getShortcut(launcher, itemInfo);
+}
diff --git a/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java b/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java
new file mode 100644
index 0000000..8bd0fec
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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 com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+import java.util.stream.Stream;
+
+/**
+ * The Launcher variant used for Android Go Edition
+ */
+public class Launcher3QuickStepGo extends QuickstepLauncher {
+    private static final String TAG = "Launcher3QuickStepGo";
+
+    @Override
+    public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+        Stream<SystemShortcut.Factory> shortcuts = super.getSupportedShortcuts();
+
+        if (AppSharing.ENABLE_APP_SHARING) {
+            shortcuts = Stream.concat(shortcuts, Stream.of(AppSharing.SHORTCUT_FACTORY));
+        }
+
+        return shortcuts;
+    }
+}
diff --git a/quickstep/res/drawable/default_sandbox_app_icon.xml b/quickstep/res/drawable/default_sandbox_app_icon.xml
new file mode 100644
index 0000000..e9c9701
--- /dev/null
+++ b/quickstep/res/drawable/default_sandbox_app_icon.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+  <solid android:color="@color/gesture_tutorial_fake_task_view_color" />
+</shape>
diff --git a/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml b/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
new file mode 100644
index 0000000..9d4cc95
--- /dev/null
+++ b/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="@color/gesture_tutorial_fake_task_view_color" />
+</shape>
diff --git a/quickstep/res/drawable/default_sandbox_wallpaper.xml b/quickstep/res/drawable/default_sandbox_wallpaper.xml
new file mode 100644
index 0000000..816d1d6
--- /dev/null
+++ b/quickstep/res/drawable/default_sandbox_wallpaper.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="?android:attr/colorBackground" />
+</shape>
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 52475df..2ff3a5e 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -16,8 +16,7 @@
 <com.android.quickstep.interaction.RootSandboxLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="?android:attr/colorBackground">
+    android:layout_height="match_parent">
 
     <View
         android:id="@+id/gesture_tutorial_ripple_view"
@@ -29,15 +28,12 @@
         android:id="@+id/gesture_tutorial_fake_icon_view"
         android:layout_width="20dp"
         android:layout_height="20dp"
-        android:background="@drawable/bg_circle"
-        android:backgroundTint="@color/gesture_tutorial_fake_task_view_color"
         android:visibility="invisible" />
 
     <View
         android:id="@+id/gesture_tutorial_fake_task_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/gesture_tutorial_fake_task_view_color"
         android:visibility="invisible" />
 
     <ImageView
diff --git a/quickstep/res/xml/overview_file_provider_paths.xml b/quickstep/res/xml/overview_file_provider_paths.xml
index 14d7459..07e3236 100644
--- a/quickstep/res/xml/overview_file_provider_paths.xml
+++ b/quickstep/res/xml/overview_file_provider_paths.xml
@@ -2,4 +2,5 @@
 <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <cache-path name="shared_images" path="/" />
     <files-path name="log_files" path="/" />
+    <root-path name="apps" path="/" />
 </paths>
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index c59c045..4dbc0f3 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -19,7 +19,9 @@
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.TRANSITION_OPEN_LAUNCHER;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -52,6 +54,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
@@ -92,7 +95,7 @@
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.InputConsumerProxy;
 import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.RectFSpringAnim;
@@ -108,6 +111,7 @@
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.systemui.shared.system.TaskInfoCompat;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
@@ -1498,17 +1502,36 @@
     }
 
     private void launchOtherTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps) {
-        TaskView taskView = mRecentsView.getTaskView(taskId);
-        if (taskView == null) {
-            return;
-        }
-
         AnimatorSet anim = new AnimatorSet();
-        TaskViewUtils.composeRecentsLaunchAnimator(
-                anim, taskView, apps,
-                mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
-                mActivity.getStateManager(), mRecentsView,
-                mActivityInterface.getDepthController());
+        TaskView taskView = mRecentsView.getTaskView(taskId);
+        if (taskView == null || !mRecentsView.isTaskViewVisible(taskView)) {
+            // TODO: Refine this animation.
+            SurfaceTransactionApplier surfaceApplier =
+                    new SurfaceTransactionApplier(mActivity.getDragLayer());
+            ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+            appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+            appAnimator.setInterpolator(ACCEL_DEACCEL);
+            appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+                @Override
+                public void onUpdate(float percent) {
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(
+                            apps[apps.length - 1].leash);
+                    Matrix matrix = new Matrix();
+                    matrix.postScale(percent, percent);
+                    matrix.postTranslate(mDp.widthPx * (1 - percent) / 2,
+                            mDp.heightPx * (1 - percent) / 2);
+                    builder.withAlpha(percent).withMatrix(matrix);
+                    surfaceApplier.scheduleApply(builder.build());
+                }
+            });
+            anim.play(appAnimator);
+        } else {
+            TaskViewUtils.composeRecentsLaunchAnimator(
+                    anim, taskView, apps,
+                    mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
+                    mActivity.getStateManager(), mRecentsView,
+                    mActivityInterface.getDepthController());
+        }
         anim.addListener(new AnimatorListenerAdapter(){
 
             @Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index 22bd334..b10bdde 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -54,6 +54,7 @@
     private final PointF mLastPos = new PointF();
 
     private boolean mPassedSlop;
+    private boolean mIsStopGesture;
 
     public OneHandedModeInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
             InputConsumer delegate, InputMonitorCompat inputMonitor) {
@@ -105,7 +106,7 @@
                     float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
                             mLastPos.y - mDownPos.y);
                     if (distance > mDragDistThreshold && mPassedSlop) {
-                        onStopGestureDetected();
+                        mIsStopGesture = true;
                     }
                 }
                 break;
@@ -113,15 +114,14 @@
             case ACTION_UP: {
                 if (mLastPos.y >= mDownPos.y && mPassedSlop) {
                     onStartGestureDetected();
+                } else if (mIsStopGesture) {
+                    onStopGestureDetected();
                 }
-
-                mPassedSlop = false;
-                mState = STATE_INACTIVE;
+                clearState();
                 break;
             }
             case ACTION_CANCEL:
-                mPassedSlop = false;
-                mState = STATE_INACTIVE;
+                clearState();
                 break;
         }
 
@@ -130,6 +130,12 @@
         }
     }
 
+    private void clearState() {
+        mPassedSlop = false;
+        mState = STATE_INACTIVE;
+        mIsStopGesture = false;
+    }
+
     private void onStartGestureDetected() {
         if (mDeviceState.isOneHandedModeEnabled()) {
             if (!mDeviceState.isOneHandedModeActive()) {
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
index d54efc5..4c1a595 100644
--- a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
@@ -46,7 +46,7 @@
     @Nullable
     @Override
     Integer getActionButtonStringId() {
-        return R.string.gesture_tutorial_action_button_label_done;
+        return null;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 2198ade..0d5a110 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -25,8 +25,10 @@
 import android.widget.TextView;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.DrawableRes;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.views.ClipIconView;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
@@ -63,13 +65,13 @@
         mTutorialType = tutorialType;
         mContext = mTutorialFragment.getContext();
 
-        View rootView = tutorialFragment.getRootView();
+        RootSandboxLayout rootView = tutorialFragment.getRootView();
         mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
         mCloseButton.setOnClickListener(button -> mTutorialFragment.closeTutorial());
         mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
         mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
         mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
-        mLauncherView = tutorialFragment.getLauncherView();
+        mLauncherView = getMockLauncherView();
         mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
         mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
         mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
@@ -84,6 +86,15 @@
         mHideFeedbackRunnable =
                 () -> mFeedbackView.animate().alpha(0).setDuration(FEEDBACK_ANIMATION_MS)
                         .withEndAction(this::showHandCoachingAnimation).start();
+
+        if (mLauncherView != null) {
+            rootView.addView(mLauncherView, 0);
+        }
+        if (mContext != null) {
+            rootView.setBackground(mContext.getDrawable(getMockWallpaperResId()));
+            mFakeTaskView.setBackground(mContext.getDrawable(getMockAppTaskThumbnailResId()));
+            mFakeIconView.setBackground(mContext.getDrawable(getMockAppIconResId()));
+        }
     }
 
     void setTutorialType(TutorialType tutorialType) {
@@ -110,6 +121,28 @@
         return null;
     }
 
+    @DrawableRes
+    protected int getMockAppTaskThumbnailResId() {
+        return R.drawable.default_sandbox_app_task_thumbnail;
+    }
+
+    @Nullable
+    public View getMockLauncherView() {
+        InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext);
+
+        return new SandboxLauncherRenderer(mContext, dp, true).getRenderedView();
+    }
+
+    @DrawableRes
+    public int getMockAppIconResId() {
+        return R.drawable.default_sandbox_app_icon;
+    }
+
+    @DrawableRes
+    public int getMockWallpaperResId() {
+        return R.drawable.default_sandbox_wallpaper;
+    }
+
     void showFeedback(int resId) {
         hideHandCoachingAnimation();
         mFeedbackView.setText(resId);
@@ -167,10 +200,11 @@
 
         if (isComplete()) {
             hideHandCoachingAnimation();
-            mLauncherView.setVisibility(View.INVISIBLE);
         } else {
             showHandCoachingAnimation();
-            mLauncherView.setVisibility(View.VISIBLE);
+        }
+        if (mLauncherView != null) {
+            mLauncherView.setVisibility(isComplete() ? View.INVISIBLE : View.VISIBLE);
         }
     }
 
@@ -213,8 +247,7 @@
         return mTutorialType == TutorialType.BACK_NAVIGATION_COMPLETE
                 || mTutorialType == TutorialType.HOME_NAVIGATION_COMPLETE
                 || mTutorialType == TutorialType.OVERVIEW_NAVIGATION_COMPLETE
-                || mTutorialType == TutorialType.ASSISTANT_COMPLETE
-                || mTutorialType == TutorialType.SANDBOX_MODE;
+                || mTutorialType == TutorialType.ASSISTANT_COMPLETE;
     }
 
     /** Denotes the type of the tutorial. */
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 608fe72..413387e 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -31,7 +31,6 @@
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
@@ -42,11 +41,10 @@
 
     TutorialType mTutorialType;
     @Nullable TutorialController mTutorialController = null;
-    View mRootView;
+    RootSandboxLayout mRootView;
     @Nullable TutorialHandAnimation mHandCoachingAnimation = null;
     EdgeBackGestureHandler mEdgeBackGestureHandler;
     NavBarGestureHandler mNavBarGestureHandler;
-    private View mLauncherView;
 
     public static TutorialFragment newInstance(TutorialType tutorialType) {
         TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
@@ -114,7 +112,8 @@
             @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         super.onCreateView(inflater, container, savedInstanceState);
 
-        mRootView = inflater.inflate(R.layout.gesture_tutorial_fragment, container, false);
+        mRootView = (RootSandboxLayout) inflater.inflate(
+                R.layout.gesture_tutorial_fragment, container, false);
         mRootView.setOnApplyWindowInsetsListener((view, insets) -> {
             Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());
             mEdgeBackGestureHandler.setInsets(systemInsets.left, systemInsets.right);
@@ -126,9 +125,6 @@
             mHandCoachingAnimation =
                 new TutorialHandAnimation(getContext(), mRootView, handAnimationResId);
         }
-        InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(getContext());
-        mLauncherView = new SandboxLauncherRenderer(getContext(), dp, true).getRenderedView();
-        ((ViewGroup) mRootView).addView(mLauncherView, 0);
         return mRootView;
     }
 
@@ -161,7 +157,7 @@
     }
 
     void onAttachedToWindow() {
-        mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView());
+        mEdgeBackGestureHandler.setViewGroupParent(getRootView());
     }
 
     void onDetachedFromWindow() {
@@ -186,14 +182,10 @@
         super.onSaveInstanceState(savedInstanceState);
     }
 
-    View getRootView() {
+    RootSandboxLayout getRootView() {
         return mRootView;
     }
 
-    View getLauncherView() {
-        return mLauncherView;
-    }
-
     @Nullable TutorialHandAnimation getHandAnimation() {
         return mHandCoachingAnimation;
     }
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 0bb0bbc..3157865 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -16,7 +16,13 @@
 
 package com.android.quickstep.logging;
 
+import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
 import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_5;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED;
@@ -34,7 +40,6 @@
 
 import com.android.launcher3.AutoInstallsLayout;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
@@ -69,7 +74,7 @@
         mLoggablePrefs = loadPrefKeys(context);
         mNavMode = SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
 
-        Utilities.getPrefs(context).registerOnSharedPreferenceChangeListener(this);
+        getPrefs(context).registerOnSharedPreferenceChangeListener(this);
         getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
 
         SecureSettingsObserver dotsObserver =
@@ -125,7 +130,8 @@
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
-        if (LAST_PREDICTION_ENABLED_STATE.equals(key) || mLoggablePrefs.containsKey(key)) {
+        if (LAST_PREDICTION_ENABLED_STATE.equals(key) || KEY_MIGRATION_SRC_HOTSEAT_COUNT.equals(key)
+                || mLoggablePrefs.containsKey(key)) {
             dispatchUserEvent();
         }
     }
@@ -142,7 +148,28 @@
                 ? LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
                 : LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED);
 
-        SharedPreferences prefs = Utilities.getPrefs(mContext);
+        SharedPreferences prefs = getPrefs(mContext);
+        StatsLogManager.LauncherEvent gridSizeChangedEvent = null;
+        switch (prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1)) {
+            case 5:
+                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_5;
+                break;
+            case 4:
+                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_4;
+                break;
+            case 3:
+                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_3;
+                break;
+            case 2:
+                gridSizeChangedEvent = LAUNCHER_GRID_SIZE_2;
+                break;
+            default:
+                // Ignore illegal input.
+                break;
+        }
+        if (gridSizeChangedEvent != null) {
+            logger.log(gridSizeChangedEvent);
+        }
         mLoggablePrefs.forEach((key, lp) -> logger.log(() ->
                 prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
     }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 3230348..1b55496 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -36,6 +36,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -418,15 +419,21 @@
                 if (freezeTaskList) {
                     ActivityOptionsCompat.setFreezeRecentTasksList(opts);
                 }
-                ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
-                        opts, (success) -> {
-                            if (resultCallback != null && !success) {
-                                // If the call to start activity failed, then post the result
-                                // immediately, otherwise, wait for the animation start callback
-                                // from the activity options above
-                                resultCallbackHandler.post(() -> resultCallback.accept(false));
-                            }
-                        }, resultCallbackHandler);
+                UI_HELPER_EXECUTOR.execute(
+                        () -> ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
+                                mTask.key,
+                                opts,
+                                (success) -> {
+                                    if (resultCallback != null && !success) {
+                                        // If the call to start activity failed, then post the
+                                        // result
+                                        // immediately, otherwise, wait for the animation start
+                                        // callback
+                                        // from the activity options above
+                                        resultCallbackHandler.post(
+                                                () -> resultCallback.accept(false));
+                                    }
+                                }, resultCallbackHandler));
             }
         }
     }
diff --git a/res/values/config.xml b/res/values/config.xml
index 46b8c23..41d1a12 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -111,6 +111,7 @@
     <!-- QSB IDs. DO not change -->
     <item type="id" name="search_container_workspace" />
     <item type="id" name="search_container_all_apps" />
+    <item type="id" name="search_container_hotseat" />
 
     <!-- Recents -->
     <item type="id" name="overview_panel"/>
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 0d90602..52f0a4a 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -42,7 +42,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
@@ -108,20 +107,10 @@
 
     private void updateTheme() {
         if (mThemeRes != Themes.getActivityThemeRes(this)) {
-            // Workaround (b/162812884): The system currently doesn't allow recreating an activity
-            // when it is not resumed, in such a case defer recreation until it is possible
-            if (hasBeenResumed()) {
-                recreate();
-            } else {
-                addOnResumeCallback(this::recreate);
-            }
+            recreate();
         }
     }
 
-    protected void addOnResumeCallback(OnResumeCallback callback) {
-        // To be overridden
-    }
-
     @Override
     public void onActionModeStarted(ActionMode mode) {
         super.onActionModeStarted(mode);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index e4bdb39..4f4f2a7 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -27,6 +27,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.config.FeatureFlags;
+
 import java.util.function.Consumer;
 
 /**
@@ -38,7 +40,8 @@
     private boolean mHasVerticalHotseat;
     private Workspace mWorkspace;
     private boolean mSendTouchToWorkspace;
-    @Nullable private Consumer<Boolean> mOnVisibilityAggregatedCallback;
+    @Nullable
+    private Consumer<Boolean> mOnVisibilityAggregatedCallback;
 
     public Hotseat(Context context) {
         this(context, null);
@@ -73,8 +76,9 @@
         if (hasVerticalHotseat) {
             setGridSize(1, idp.numHotseatIcons);
         } else {
-            setGridSize(idp.numHotseatIcons, 1);
+            setGridSize(idp.numHotseatIcons, FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 2 : 1);
         }
+        showInlineQsb();
     }
 
     @Override
@@ -97,7 +101,11 @@
             lp.height = grid.hotseatBarSizePx + insets.bottom;
         }
         Rect padding = grid.getHotseatLayoutPadding();
-        setPadding(padding.left, padding.top, padding.right, padding.bottom);
+        int paddingBottom = padding.bottom;
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !grid.isVerticalBarLayout()) {
+            paddingBottom -= grid.hotseatBarBottomPaddingPx;
+        }
+        setPadding(padding.left, padding.top, padding.right, paddingBottom);
 
         setLayoutParams(lp);
         InsettableFrameLayout.dispatchInsets(this, insets);
@@ -148,4 +156,8 @@
     public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
         mOnVisibilityAggregatedCallback = callback;
     }
+
+    protected void showInlineQsb() {
+        //Does nothing
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 058eca8..ea98157 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
@@ -221,6 +222,8 @@
 
     static final boolean DEBUG_STRICT_MODE = false;
 
+    private static final boolean ENABLE_ACTIVITY_CROSSFADE = false;
+
     private static final int REQUEST_CREATE_SHORTCUT = 1;
     private static final int REQUEST_CREATE_APPWIDGET = 5;
 
@@ -1134,7 +1137,11 @@
         int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal);
         LauncherState[] stateValues = LauncherState.values();
         LauncherState state = stateValues[stateOrdinal];
-        if (!state.shouldDisableRestore()) {
+
+        NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance();
+        boolean forceRestore = lastInstance != null
+                && (lastInstance.config.diff(mOldConfig) & CONFIG_UI_MODE) != 0;
+        if (forceRestore || !state.shouldDisableRestore()) {
             mStateManager.goToState(state, false /* animated */);
         }
 
@@ -1373,14 +1380,18 @@
 
     @Override
     public Object onRetainNonConfigurationInstance() {
+        NonConfigInstance instance = new NonConfigInstance();
+        instance.config = new Configuration(mOldConfig);
+
         int width = mDragLayer.getWidth();
         int height = mDragLayer.getHeight();
 
-        if (width <= 0 || height <= 0) {
-            return null;
+        // TODO: b/172467144 Remove hardcoded ENABLE_ACTIVITY_CROSSFADE.
+        if (ENABLE_ACTIVITY_CROSSFADE && width > 0 && height > 0) {
+            instance.snapshot =
+                    BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
         }
-
-        return BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
+        return instance;
     }
 
     public AllAppsTransitionController getAllAppsController() {
@@ -1974,7 +1985,6 @@
         return result;
     }
 
-    @Override
     public void addOnResumeCallback(OnResumeCallback callback) {
         mOnResumeCallbacks.add(callback);
     }
@@ -2340,9 +2350,7 @@
             if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
                 // Verify that we own the widget
                 if (appWidgetInfo == null) {
-                    FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId
-                            + ",title=" + item.title
-                            + ",providerName=" + item.providerName.toShortString());
+                    FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                     getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
                     return null;
                 }
@@ -2786,15 +2794,14 @@
      * updates.
      */
     private void crossFadeWithPreviousAppearance() {
-        Bitmap previousAppearanceBitmap = (Bitmap) getLastNonConfigurationInstance();
+        NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance();
 
-        if (previousAppearanceBitmap == null) {
+        if (lastInstance == null || lastInstance.snapshot == null) {
             return;
         }
 
         ImageView crossFadeHelper = new ImageView(this);
-
-        crossFadeHelper.setImageBitmap(previousAppearanceBitmap);
+        crossFadeHelper.setImageBitmap(lastInstance.snapshot);
         crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
 
         InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
@@ -2814,4 +2821,9 @@
                 .withEndAction(() -> getRootView().removeView(crossFadeHelper))
                 .start();
     }
+
+    private static class NonConfigInstance {
+        public Configuration config;
+        public Bitmap snapshot;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6af248c..aeed16a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -85,7 +85,6 @@
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
@@ -932,11 +931,6 @@
             final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(db,
                     Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
                     "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
-            final String allWidgetIds = Arrays.stream(allWidgets).mapToObj(String::valueOf)
-                    .collect(Collectors.joining(","));
-            final String validWidgetIds = validWidgets.getArray().toConcatString();
-            FileLog.d(TAG, "All widget ids: " + allWidgetIds);
-            FileLog.d(TAG, "Valid widget ids: " + validWidgetIds);
             for (int widgetId : allWidgets) {
                 if (!validWidgets.contains(widgetId)) {
                     try {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index eba0ac9..79476fc 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.HintState;
@@ -168,9 +169,9 @@
 
     /**
      * Returns an array of two elements.
-     *   The first specifies the scale for the overview
-     *   The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
-     *   should be shifted horizontally.
+     * The first specifies the scale for the overview
+     * The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
+     * should be shifted horizontally.
      */
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
         return launcher.getNormalOverviewScaleAndOffset();
@@ -185,10 +186,12 @@
     }
 
     public int getVisibleElements(Launcher launcher) {
-        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
+        int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
+        if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
+                && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+            flags |= HOTSEAT_SEARCH_BOX;
         }
-        return HOTSEAT_ICONS | HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR;
+        return flags;
     }
 
     /**
@@ -229,6 +232,7 @@
 
     /**
      * Returns the amount of blur and wallpaper zoom for this state with {@param isMultiWindowMode}.
+     *
      * @see #getDepth(Context).
      */
     public final float getDepth(Context context, boolean isMultiWindowMode) {
@@ -255,7 +259,7 @@
         return new PageAlphaProvider(ACCEL_2) {
             @Override
             public float getPageAlpha(int pageIndex) {
-                return  pageIndex != centerPage ? 0 : 1f;
+                return pageIndex != centerPage ? 0 : 1f;
             }
         };
     }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 6326b7a..ee0c7bb 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -36,7 +36,8 @@
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private final int[] mTmpCellXY = new int[2];
 
-    @ContainerType private final int mContainerType;
+    @ContainerType
+    private final int mContainerType;
     private final WallpaperManager mWallpaperManager;
 
     private int mCellWidth;
@@ -44,7 +45,7 @@
 
     private int mCountX;
 
-    private ActivityContext mActivity;
+    private final ActivityContext mActivity;
     private boolean mInvertIfRtl = false;
 
     public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
@@ -79,7 +80,7 @@
         int count = getChildCount();
 
         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
         setMeasuredDimension(widthSpecSize, heightSpecSize);
 
         for (int i = 0; i < count; i++) {
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 000ccbb..bd2f04d 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.allapps.AlphabeticalAppsList;
 import com.android.launcher3.allapps.SearchUiManager;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -215,7 +216,8 @@
 
     @Override
     public float getScrollRangeDelta(Rect insets) {
-        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()
+                || FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
             return 0;
         } else {
             return insets.bottom + insets.top;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 00cb7b9..8f63c09 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -153,7 +153,7 @@
             "ENABLE_OVERVIEW_CONTENT_PUSH", false, "Show Content Push button in Overview Actions");
 
     public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
-            "ENABLE_DATABASE_RESTORE", true,
+            "ENABLE_DATABASE_RESTORE", false,
             "Enable database restore when new restore session is created");
 
     public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 0b445bc..2066cd3 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -332,6 +332,18 @@
 
         @UiEvent(doc = "Notification dismissed by swiping right.")
         LAUNCHER_NOTIFICATION_DISMISSED(652),
+
+        @UiEvent(doc = "Current grid size is changed to 5.")
+        LAUNCHER_GRID_SIZE_5(662),
+
+        @UiEvent(doc = "Current grid size is changed to 4.")
+        LAUNCHER_GRID_SIZE_4(663),
+
+        @UiEvent(doc = "Current grid size is changed to 3.")
+        LAUNCHER_GRID_SIZE_3(664),
+
+        @UiEvent(doc = "Current grid size is changed to 2.")
+        LAUNCHER_GRID_SIZE_2(665),
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 80a684d..b108788 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -745,11 +745,7 @@
                                             + "span=" + appWidgetInfo.spanX + "x"
                                             + appWidgetInfo.spanY + " minSpan="
                                             + widgetProviderInfo.minSpanX + "x"
-                                            + widgetProviderInfo.minSpanY
-                                            + ", appWidgetInfo.provider="
-                                            + appWidgetInfo.providerName.toShortString()
-                                            + ", widgetProviderInfo.provider="
-                                            + widgetProviderInfo.provider.toShortString());
+                                            + widgetProviderInfo.minSpanY);
                                     continue;
                                 }
                                 if (!c.isOnWorkspaceOrHotseat()) {
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index dbe5f42..601e117 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -121,7 +121,9 @@
         final int origDragLayerChildCount = dragLayer.getChildCount();
         super.onStop();
 
-        getStateManager().moveToRestState();
+        if (!isChangingConfigurations()) {
+            getStateManager().moveToRestState();
+        }
 
         // Workaround for b/78520668, explicitly trim memory once UI is hidden
         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);