Merge "Jank: not blocking UI thread when starting a task"
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/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/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 2aed76a..9da306e 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -312,8 +312,10 @@
             windowAnimEndListener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    stateManager.moveToRestState();
-                    stateManager.reapplyState();
+                    recentsView.post(() -> {
+                        stateManager.moveToRestState();
+                        stateManager.reapplyState();
+                    });
                 }
             };
         } else {
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/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/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index a8e0cb3..4e811f3 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1,15 +1,10 @@
 sdk=29
 shadows= \
-    com.android.launcher3.shadows.LShadowApplicationPackageManager \
     com.android.launcher3.shadows.LShadowAppPredictionManager \
     com.android.launcher3.shadows.LShadowAppWidgetManager \
     com.android.launcher3.shadows.LShadowBackupManager \
-    com.android.launcher3.shadows.LShadowBitmap \
     com.android.launcher3.shadows.LShadowDisplay \
     com.android.launcher3.shadows.LShadowLauncherApps \
-    com.android.launcher3.shadows.LShadowTypeface \
-    com.android.launcher3.shadows.LShadowUserManager \
-    com.android.launcher3.shadows.LShadowWallpaperManager \
     com.android.launcher3.shadows.ShadowDeviceFlag \
     com.android.launcher3.shadows.ShadowLooperExecutor \
     com.android.launcher3.shadows.ShadowMainThreadInitializedObject \
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 90313ab..8baf5a3 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.shadows.LShadowBackupManager;
-import com.android.launcher3.shadows.LShadowUserManager;
 import com.android.launcher3.util.LauncherModelHelper;
 
 import org.junit.Before;
@@ -51,6 +50,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
 
 /**
  * Tests to verify backup and restore flow.
@@ -68,7 +68,7 @@
     private static final int FLAG_SYSTEM = 0x00000800;
     private static final int FLAG_PROFILE = 0x00001000;
 
-    private LShadowUserManager mUserManager;
+    private ShadowUserManager mUserManager;
     private BackupManager mBackupManager;
     private LauncherModelHelper mModelHelper;
     private SQLiteDatabase mDb;
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
deleted file mode 100644
index da8b919..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.shadows;
-
-import android.os.Process;
-import android.os.UserHandle;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowApplicationPackageManager;
-
-/**
- * Shadow for {@link ShadowApplicationPackageManager} which create mock predictors
- */
-@Implements(className = "android.app.ApplicationPackageManager")
-public class LShadowApplicationPackageManager extends ShadowApplicationPackageManager {
-
-    @Implementation
-    public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
-        return Process.myUserHandle().equals(user) ? label : "Work " + label;
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java
deleted file mode 100644
index abd90bb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowBitmap;
-
-/**
- * Extension of {@link ShadowBitmap} with missing shadow methods
- */
-@Implements(value = Bitmap.class)
-public class LShadowBitmap extends ShadowBitmap {
-
-    @Implementation
-    protected Bitmap extractAlpha(Paint paint, int[] offsetXY) {
-        return extractAlpha();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
deleted file mode 100644
index 0e7c1de..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.shadows;
-
-import android.graphics.Typeface;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowTypeface;
-
-/**
- * Extension of {@link ShadowTypeface} with missing shadow methods
- */
-@Implements(Typeface.class)
-public class LShadowTypeface extends ShadowTypeface {
-
-    @Implementation
-    public static Typeface create(Typeface family, int weight, boolean italic) {
-        int style = italic ? Typeface.ITALIC : Typeface.NORMAL;
-        if (weight >= 400) {
-            style |= Typeface.BOLD;
-        }
-        return create(family, style);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
deleted file mode 100644
index edf8edb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.SparseBooleanArray;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowUserManager;
-
-/**
- * Extension of {@link ShadowUserManager} with missing shadow methods
- */
-@Implements(value = UserManager.class)
-public class LShadowUserManager extends ShadowUserManager {
-
-    private final SparseBooleanArray mQuietUsers = new SparseBooleanArray();
-    private final SparseBooleanArray mLockedUsers = new SparseBooleanArray();
-
-    @Implementation
-    protected boolean isQuietModeEnabled(UserHandle userHandle) {
-        return mQuietUsers.get(userHandle.hashCode());
-    }
-
-    public void setQuietModeEnabled(UserHandle userHandle, boolean enabled) {
-        mQuietUsers.put(userHandle.hashCode(), enabled);
-    }
-
-    @Implementation
-    protected boolean isUserUnlocked(UserHandle userHandle) {
-        return !mLockedUsers.get(userHandle.hashCode());
-    }
-
-    public void setUserLocked(UserHandle userHandle, boolean enabled) {
-        mLockedUsers.put(userHandle.hashCode(), enabled);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
deleted file mode 100644
index d60251c..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2019 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.shadows;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadows.ShadowWallpaperManager;
-
-/**
- * Extension of {@link ShadowUserManager} with missing shadow methods
- */
-@Implements(WallpaperManager.class)
-public class LShadowWallpaperManager extends ShadowWallpaperManager {
-
-    @Implementation
-    protected static WallpaperManager getInstance(Context context) {
-        return context.getSystemService(WallpaperManager.class);
-    }
-
-    /**
-     * Remove this once the fix for
-     * https://github.com/robolectric/robolectric/issues/5285
-     * is available
-     */
-    public static void initializeMock() {
-        WallpaperManager wm = mock(WallpaperManager.class);
-        ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
-        shadowApplication.setSystemService(Context.WALLPAPER_SERVICE, wm);
-        doReturn(0).when(wm).getDesiredMinimumWidth();
-        doReturn(0).when(wm).getDesiredMinimumHeight();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
index 6dd4df8..efac150 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
@@ -19,7 +19,6 @@
 
 import android.app.Application;
 
-import com.android.launcher3.shadows.LShadowWallpaperManager;
 import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
 import com.android.launcher3.shadows.ShadowOverrides;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -37,9 +36,6 @@
 
         // Disable plugins
         PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
-
-        // Initialize mock wallpaper manager
-        LShadowWallpaperManager.initializeMock();
     }
 
     @Override
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 8bf027d..b85c648 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -106,7 +106,8 @@
                 }
             }
             // attempt to update widget id in backup table as well
-            new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams(where, args))
+            new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams(
+                    "appWidgetId=? and profileId=?", args))
                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
                     .put(LauncherSettings.Favorites.RESTORED, state)
                     .commit();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 058eca8..ca4f6ee 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -221,6 +221,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;
 
@@ -1376,7 +1378,8 @@
         int width = mDragLayer.getWidth();
         int height = mDragLayer.getHeight();
 
-        if (width <= 0 || height <= 0) {
+        // TODO: b/172467144 Remove hardcoded ENABLE_ACTIVITY_CROSSFADE.
+        if (!ENABLE_ACTIVITY_CROSSFADE || width <= 0 || height <= 0) {
             return null;
         }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8e6c2a7..00cb7b9 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -160,9 +160,10 @@
             "ENABLE_SMARTSPACE_UNIVERSAL", false,
             "Replace Smartspace with a version rendered by System UI.");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_BLUECHIP = getDebugFlag(
-            "ENABLE_SMARTSPACE_BLUECHIP", false,
-            "Replace Smartspace with the Bluechip version. Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
+    public static final BooleanFlag ENABLE_SMARTSPACE_ENHANCED = getDebugFlag(
+            "ENABLE_SMARTSPACE_ENHANCED", false,
+            "Replace Smartspace with the enhanced version. "
+              + "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
 
     public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag(
             "ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
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/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 2adf8ce..b496608 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -20,7 +20,6 @@
 import android.app.ActivityOptions;
 import android.content.Intent;
 import android.os.Bundle;
-import android.view.Display;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewAnimationUtils;
@@ -170,9 +169,7 @@
 
     @Override
     public ActivityOptions getActivityLaunchOptions(View v) {
-        final Display display = getWindow().getDecorView().getDisplay();
-        return display != null ? ActivityOptions.makeBasic().setLaunchDisplayId(
-                       display.getDisplayId()) : null;
+        return null;
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 6a83332..804fb3e 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.views;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
@@ -288,6 +290,7 @@
         if (!sectionName.equals(mPopupSectionName)) {
             mPopupSectionName = sectionName;
             mPopupView.setText(sectionName);
+            performHapticFeedback(CLOCK_TICK);
         }
         animatePopupVisibility(!sectionName.isEmpty());
         mLastTouchY = boundedY;
diff --git a/src/com/android/launcher3/views/SearchResultIcon.java b/src/com/android/launcher3/views/SearchResultIcon.java
index 51c741b..9bcfb8c 100644
--- a/src/com/android/launcher3/views/SearchResultIcon.java
+++ b/src/com/android/launcher3/views/SearchResultIcon.java
@@ -111,6 +111,7 @@
     public void applySearchTarget(SearchTarget searchTarget) {
         mSearchTarget = searchTarget;
         SearchEventTracker.getInstance(getContext()).registerWeakHandler(mSearchTarget, this);
+        setVisibility(VISIBLE);
         switch (searchTarget.getItemType()) {
             case TARGET_TYPE_APP:
             case TARGET_TYPE_HERO_APP:
@@ -131,6 +132,10 @@
     private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
         AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
         AppInfo appInfo = appsStore.getApp(new ComponentKey(componentName, userHandle));
+        if (appInfo == null) {
+            setVisibility(GONE);
+            return;
+        }
         applyFromApplicationInfo(appInfo);
         notifyItemInfoChanged(appInfo);
     }
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index df0770d..1e1cf04 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -45,6 +45,7 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
@@ -71,7 +72,8 @@
 
     @After
     public void removeWorkProfile() throws Exception {
-        Log.d(TestProtocol.WORK_PROFILE_REMOVED, "(teardown) removing uid" + mProfileUserId);
+        Log.d(TestProtocol.WORK_PROFILE_REMOVED, "(teardown) removing uid" + mProfileUserId,
+                new Exception());
         mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
     }
 
@@ -142,12 +144,16 @@
                 "work profile status (" + mProfileUserId + ") :"
                         + launcher.getAppsView().isWorkTabVisible()));
 
+        AtomicInteger attempt = new AtomicInteger(0);
         // verify work edu is seen next
-        waitForLauncherCondition("Launcher did not show the next edu screen", l ->
-                ((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage() == WORK_PAGE
-                        && ((TextView) workEduView.findViewById(
-                        R.id.content_text)).getText().equals(
-                        l.getResources().getString(R.string.work_profile_edu_work_apps)));
+        waitForLauncherCondition("Launcher did not show the next edu screen", l -> {
+            Log.d(TestProtocol.WORK_PROFILE_REMOVED,
+                    "running test attempt" + attempt.getAndIncrement());
+            return ((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage()
+                    == WORK_PAGE && ((TextView) workEduView.findViewById(
+                    R.id.content_text)).getText().equals(
+                    l.getResources().getString(R.string.work_profile_edu_work_apps));
+        });
     }
 
     @Test
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 22833ec..3c89cfd 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -105,6 +105,7 @@
     static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
     static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
     private final String mLauncherPackage;
+    private final boolean mIsLauncher3;
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -205,6 +206,7 @@
     public LauncherInstrumentation(Instrumentation instrumentation) {
         mInstrumentation = instrumentation;
         mDevice = UiDevice.getInstance(instrumentation);
+        mIsLauncher3 = "com.android.launcher3".equals(getLauncherPackageName());
 
         // Launcher should run in test harness so that custom accessibility protocol between
         // Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
@@ -1396,7 +1398,7 @@
     }
 
     boolean isLauncher3() {
-        return "com.android.launcher3".equals(getLauncherPackageName());
+        return mIsLauncher3;
     }
 
     void expectEvent(String sequence, Pattern expected) {