Merge "Inline TaskView#resetVisualProperties() into RecentsView" into ub-launcher3-rvc-dev
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 527bfc3..60afddb 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -52,7 +52,7 @@
             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
-            android:taskAffinity="${packageName}.launcher"
+            android:taskAffinity=""
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index b2286f1..e49f2ec 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -22,11 +22,16 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.launcher3" >
 
+    <permission
+        android:name="${packageName}.permission.HOTSEAT_EDU"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signatureOrSystem" />
+
     <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
 
-    
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
         android:fullBackupOnly="true"
@@ -99,13 +104,24 @@
             android:name="com.android.quickstep.interaction.GestureSandboxActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
-            android:taskAffinity="${packageName}.launcher"
             android:screenOrientation="portrait">
             <intent-filter>
                 <action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".hybridhotseat.HotseatEduActivity"
+            android:theme="@android:style/Theme.NoDisplay"
+            android:noHistory="true"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:permission="${packageName}.permission.HOTSEAT_EDU">
+            <intent-filter>
+                <action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
 
     </application>
 
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index b9621e4..36c9b00 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -72,33 +72,42 @@
                 android:layout_height="0dp"
                 launcher:containerType="hotseat" />
 
-            <FrameLayout
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:paddingLeft="@dimen/bottom_sheet_edu_padding"
                 android:paddingTop="8dp"
                 android:paddingRight="@dimen/bottom_sheet_edu_padding">
 
-                <Button
-                    android:id="@+id/turn_predictions_on"
-                    android:layout_width="wrap_content"
+                <FrameLayout
+                    android:layout_width="0dp"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="end"
-                    android:background="?android:attr/selectableItemBackground"
-                    android:text="@string/hotseat_edu_accept"
-                    android:textAlignment="textEnd"
-                    android:textColor="@android:color/white" />
-
-                <Button
-                    android:layout_width="wrap_content"
+                    android:layout_gravity="center_vertical"
+                    android:layout_weight=".4">
+                    <Button
+                        android:id="@+id/no_thanks"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:background="?android:attr/selectableItemBackground"
+                        android:text="@string/hotseat_edu_dismiss"
+                        android:textColor="@android:color/white"/>
+                </FrameLayout>
+                <FrameLayout
+                    android:layout_width="0dp"
                     android:layout_height="wrap_content"
-                    android:id="@+id/no_thanks"
-                    android:text="@string/hotseat_edu_dismiss"
-                    android:layout_gravity="start"
-                    android:background="?android:attr/selectableItemBackground"
-                    android:textColor="@android:color/white" />
+                    android:layout_gravity="center_vertical"
+                    android:layout_weight=".6">
+                    <Button
+                        android:id="@+id/turn_predictions_on"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:background="?android:attr/selectableItemBackground"
+                        android:gravity="end"
+                        android:text="@string/hotseat_edu_accept"
+                        android:textColor="@android:color/white"/>
+                </FrameLayout>
 
-            </FrameLayout>
+            </LinearLayout>
         </LinearLayout>
     </LinearLayout>
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index f881610..5e54cd2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3.appprediction;
 
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 
@@ -23,9 +26,9 @@
 import android.app.prediction.AppTarget;
 import android.content.ComponentName;
 import android.content.Context;
-import android.os.Process;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
@@ -36,7 +39,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
-import com.android.launcher3.hybridhotseat.HotseatFileLog;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.model.data.ItemInfo;
@@ -50,6 +52,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
 import java.util.stream.IntStream;
 
 /**
@@ -304,6 +308,26 @@
     }
 
     /**
+     * Returns ranking info for the app within all apps prediction.
+     * Only applicable when {@link ItemInfo#itemType} is one of the followings:
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
+     */
+    public OptionalInt getAllAppsRank(@Nullable ItemInfo itemInfo) {
+        Optional<ComponentKey> componentKey = Optional.ofNullable(itemInfo)
+                .filter(item -> item.itemType == ITEM_TYPE_APPLICATION
+                        || item.itemType == ITEM_TYPE_SHORTCUT
+                        || item.itemType == ITEM_TYPE_DEEP_SHORTCUT)
+                .map(ItemInfo::getTargetComponent)
+                .map(componentName -> new ComponentKey(componentName, itemInfo.user));
+
+        return componentKey.map(key -> IntStream.range(0, getCurrentState().apps.size())
+                .filter(index -> key.equals(getCurrentState().apps.get(index).getComponentKey()))
+                .findFirst()).orElseGet(OptionalInt::empty);
+    }
+
+    /**
      * Fill in predicted_rank field based on app prediction.
      * Only applicable when {@link ItemInfo#itemType} is one of the followings:
      * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
@@ -313,17 +337,6 @@
     public static void fillInPredictedRank(
             @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
 
-        HotseatFileLog hotseatFileLog = HotseatFileLog.INSTANCE.getNoCreate();
-
-        if (hotseatFileLog != null && itemInfo != null && Utilities.IS_DEBUG_DEVICE) {
-            final String pkg = itemInfo.getTargetComponent() != null
-                    ? itemInfo.getTargetComponent().getPackageName() : "unknown";
-            hotseatFileLog.log("UserEvent",
-                    "appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
-                            && !Process.myUserHandle().equals(itemInfo.user))
-                            + ",launchLocation:" + itemInfo.container);
-        }
-
         final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate();
         if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null
                 || (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
new file mode 100644
index 0000000..c968de9
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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.hybridhotseat;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.ActivityTracker;
+
+/**
+ * Proxy activity to return user to home screen and show halfsheet education
+ */
+public class HotseatEduActivity extends Activity {
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(getPackageName())
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        new HotseatActivityTracker<>().addToIntent(homeIntent);
+        startActivity(homeIntent);
+        finish();
+    }
+
+    static class HotseatActivityTracker<T extends QuickstepLauncher> implements
+            ActivityTracker.SchedulerCallback {
+
+        @Override
+        public boolean init(BaseActivity activity, boolean alreadyOnHome) {
+            QuickstepLauncher launcher = (QuickstepLauncher) activity;
+            if (launcher != null && launcher.getHotseatPredictionController() != null) {
+                launcher.getHotseatPredictionController().showEdu();
+            }
+            return false;
+        }
+
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 5d807d3..c1bf2fd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -84,7 +84,7 @@
         }
         Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
                 R.string.hotseat_prediction_settings, null,
-                () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+                () -> mLauncher.startActivity(getSettingsIntent()));
     }
 
     /**
@@ -237,7 +237,7 @@
                 < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
             Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
                     R.string.hotseat_prediction_settings, null,
-                    () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+                    () -> mLauncher.startActivity(getSettingsIntent()));
         } else {
             new ArrowTipView(mLauncher).show(
                     mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
@@ -265,7 +265,7 @@
                     requiresMigration ? R.string.hotseat_tip_no_empty_slots
                             : R.string.hotseat_auto_enrolled),
                     mHotseat.getTop());
-            mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+            mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
             finishOnboarding();
         }
     }
@@ -281,5 +281,9 @@
         mActiveDialog.setHotseatEduController(this);
         mActiveDialog.show(mPredictedApps);
     }
+
+    static Intent getSettingsIntent() {
+        return new Intent(SETTINGS_ACTION).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
 }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 96be5df..2b3f395 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -112,13 +112,13 @@
 
         mHotseatEduController.moveHotseatItems();
         mHotseatEduController.finishOnboarding();
-        mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
+        mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
     }
 
     private void onDismiss(View v) {
         mHotseatEduController.showDimissTip();
         mHotseatEduController.finishOnboarding();
-        mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_DENY);
+        mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
         handleClose(true);
     }
 
@@ -212,7 +212,7 @@
         }
         AbstractFloatingView.closeAllOpenViews(mLauncher);
         attachToContainer();
-        mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_SEEN);
+        mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
         animateOpen();
         populatePreview(predictions);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 6ca07bb..7334d80 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -17,7 +17,9 @@
 
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.hybridhotseat.HotseatEduController.SETTINGS_ACTION;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -28,8 +30,9 @@
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
 import android.content.ComponentName;
-import android.content.Intent;
+import android.os.Process;
 import android.util.Log;
+import android.view.HapticFeedbackConstants;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -52,6 +55,9 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
+import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -65,6 +71,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.Snackbar;
 
@@ -116,9 +123,18 @@
     private final View.OnLongClickListener mPredictionLongClickListener = v -> {
         if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
         if (mLauncher.getWorkspace().isSwitchingState()) return false;
+        if (!mLauncher.getOnboardingPrefs().getBoolean(
+                OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
+            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+                    R.string.hotseat_prediction_settings, null,
+                    () -> mLauncher.startActivity(getSettingsIntent()));
+            mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
+            mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            return true;
+        }
         // Start the drag
         mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
-        return false;
+        return true;
     };
 
     public HotseatPredictionController(Launcher launcher) {
@@ -150,42 +166,40 @@
      * Shows appropriate hotseat education based on prediction enabled and migration states.
      */
     public void showEdu() {
-        if (mComponentKeyMappers.isEmpty()) {
-            // launcher has empty predictions set
-            Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
-                    R.string.hotseat_prediction_settings, null,
-                    () -> mLauncher.startActivity(
-                            new Intent(SETTINGS_ACTION)));
-        } else if (isEduSeen()) {
-            // user has already went through education
-            new ArrowTipView(mLauncher).show(
-                    mLauncher.getString(R.string.hotsaet_tip_prediction_enabled),
-                    mHotseat.getTop());
-        } else {
-            HotseatEduController eduController = new HotseatEduController(mLauncher, mRestoreHelper,
-                    this::createPredictor);
-            eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
-            eduController.showEdu();
-        }
+        mLauncher.getStateManager().goToState(NORMAL, true, () -> {
+            if (mComponentKeyMappers.isEmpty()) {
+                // launcher has empty predictions set
+                Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
+                        R.string.hotseat_prediction_settings, null,
+                        () -> mLauncher.startActivity(getSettingsIntent()));
+            } else if (isEduSeen() || getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
+                showDiscoveryTip();
+            } else {
+                HotseatEduController eduController = new HotseatEduController(mLauncher,
+                        mRestoreHelper,
+                        this::createPredictor);
+                eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
+                eduController.showEdu();
+            }
+        });
     }
 
     /**
      * Shows educational tip for hotseat if user does not go through Tips app.
      */
-    public void showDiscoveryTip() {
-        if (getPredictedIcons().size() == mHotSeatItemsCount) {
+    private void showDiscoveryTip() {
+        if (getPredictedIcons().isEmpty()) {
             new ArrowTipView(mLauncher).show(
                     mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
         } else {
             Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
                     R.string.hotseat_prediction_settings, null,
-                    () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+                    () -> mLauncher.startActivity(getSettingsIntent()));
         }
     }
 
     /**
      * Returns if hotseat client has predictions
-     * @return
      */
     public boolean hasPredictions() {
         return !mComponentKeyMappers.isEmpty();
@@ -358,6 +372,7 @@
         updateDependencies();
         bindItems(items, false, null);
     }
+
     private void setPredictedApps(List<AppTarget> appTargets) {
         mComponentKeyMappers.clear();
         if (appTargets.isEmpty()) {
@@ -635,6 +650,49 @@
         mHotseat.fillInLogContainerData(childInfo, child, parents);
     }
 
+    /**
+     * Logs rank info based on current list of predicted items
+     */
+    public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
+        if (Utilities.IS_DEBUG_DEVICE) {
+            final String pkg = itemInfo.getTargetComponent() != null
+                    ? itemInfo.getTargetComponent().getPackageName() : "unknown";
+            HotseatFileLog.INSTANCE.get(mLauncher).log("UserEvent",
+                    "appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
+                            && !Process.myUserHandle().equals(itemInfo.user))
+                            + ",launchLocation:" + itemInfo.container);
+        }
+
+        final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
+
+        final List<ComponentKeyMapper> predictedApps = new ArrayList<>(mComponentKeyMappers);
+        OptionalInt rank = IntStream.range(0, predictedApps.size())
+                .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
+                .findFirst();
+        if (!rank.isPresent()) {
+            return;
+        }
+
+        int cardinality = 0;
+        for (PredictedAppIcon icon : getPredictedIcons()) {
+            ItemInfo info = (ItemInfo) icon.getTag();
+            cardinality |= 1 << info.screenId;
+        }
+
+        PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
+        containerBuilder.setCardinality(cardinality);
+        if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+            containerBuilder.setIndex(rank.getAsInt());
+        }
+        mLauncher.getStatsLogManager().logger()
+                .withInstanceId(instanceId)
+                .withRank(rank.getAsInt())
+                .withContainerInfo(ContainerInfo.newBuilder()
+                        .setPredictedHotseatContainer(containerBuilder)
+                        .build())
+                .log(LAUNCHER_HOTSEAT_RANKED);
+    }
+
     private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
 
         private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 0ace4cc..597c17b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -57,7 +57,7 @@
         LauncherAccessibilityDelegate.AccessibilityActionHandler {
 
     private static final int RING_SHADOW_COLOR = 0x99000000;
-    private static final float RING_EFFECT_RATIO = 0.08f;
+    private static final float RING_EFFECT_RATIO = 0.095f;
 
     boolean mIsDrawingDot = false;
     private final DeviceProfile mDeviceProfile;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 77d71a3..7d86cea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
@@ -29,6 +30,7 @@
 
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
 
@@ -41,10 +43,12 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.hybridhotseat.HotseatEduController;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -77,6 +81,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.OptionalInt;
 import java.util.stream.Stream;
 
 public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -89,25 +94,31 @@
             SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
 
     @Override
-    protected void setupViews() {
-        super.setupViews();
-        if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
-            mHotseatPredictionController = new HotseatPredictionController(this);
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (mHotseatPredictionController != null) {
             mHotseatPredictionController.createPredictor();
         }
     }
 
     @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        if (HotseatEduController.HOTSEAT_EDU_ACTION.equals(intent.getAction())
-                && mHotseatPredictionController != null) {
-            boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags()
-                    & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
-                    != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-            getStateManager().goToState(NORMAL, alreadyOnHome, () -> {
-                mHotseatPredictionController.showEdu();
-            });
+    protected void setupViews() {
+        super.setupViews();
+        if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
+            mHotseatPredictionController = new HotseatPredictionController(this);
+        }
+    }
+
+    @Override
+    protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+        StatsLogger logger = getStatsLogManager()
+                .logger().withItemInfo(info).withInstanceId(instanceId);
+        OptionalInt allAppsRank = PredictionUiStateManager.INSTANCE.get(this).getAllAppsRank(info);
+        allAppsRank.ifPresent(logger::withRank);
+        logger.log(LAUNCHER_APP_LAUNCH_TAP);
+
+        if (mHotseatPredictionController != null) {
+            mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
         }
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index ba8656d..a0af797 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
+import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
@@ -90,6 +91,10 @@
     protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
             + MY_ANIM_COUNT;
 
+    // Due to use of physics, duration may differ between devices so we need to calculate and
+    // cache the value.
+    private int mHintToNormalDuration = -1;
+
     public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
 
     public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
@@ -221,6 +226,14 @@
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
             config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+        } else if (fromState == HINT_STATE && toState == NORMAL) {
+            config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
+            if (mHintToNormalDuration == -1) {
+                ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
+                        toState.getWorkspaceScaleAndTranslation(mActivity).scale);
+                mHintToNormalDuration = (int) va.getDuration();
+            }
+            config.duration = Math.max(config.duration, mHintToNormalDuration);
         }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 39bbfb9..a35e13a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -15,10 +15,13 @@
  */
 package com.android.launcher3.uioverrides.touchcontrollers;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
@@ -112,7 +115,8 @@
             }
             return true;
         }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+        int typeToClose = ENABLE_ALL_APPS_EDU.get() ? TYPE_ALL & ~TYPE_ALL_APPS_EDU : TYPE_ALL;
+        if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
             if (TestProtocol.sDebugTracing) {
                 Log.d(TestProtocol.PAUSE_NOT_DETECTED,
                         "NavBarToHomeTouchController.canInterceptTouch true 2 "
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 966e25b..9316938 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -24,7 +24,9 @@
 import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.graphics.PointF;
 import android.util.Log;
@@ -34,6 +36,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.graphics.OverviewScrim;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.testing.TestProtocol;
@@ -59,9 +62,14 @@
 
     private boolean mDidTouchStartInNavBar;
     private boolean mReachedOverview;
+    private boolean mIsOverviewRehidden;
+    private boolean mIsHomeStaggeredAnimFinished;
     // The last recorded displacement before we reached overview.
     private PointF mStartDisplacement = new PointF();
 
+    // Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
+    private ObjectAnimator mNormalToHintOverviewScrimAnimator;
+
     public NoButtonNavbarToOverviewTouchController(Launcher l) {
         super(l);
         mRecentsView = l.getOverviewPanel();
@@ -107,11 +115,31 @@
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
         super.onDragStart(start, startDisplacement);
-
+        if (mFromState == NORMAL && mToState == HINT_STATE) {
+            mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofFloat(
+                    mLauncher.getDragLayer().getOverviewScrim(),
+                    OverviewScrim.SCRIM_PROGRESS,
+                    mFromState.getOverviewScrimAlpha(mLauncher),
+                    mToState.getOverviewScrimAlpha(mLauncher));
+        }
         mReachedOverview = false;
     }
 
     @Override
+    protected void updateProgress(float fraction) {
+        super.updateProgress(fraction);
+        if (mNormalToHintOverviewScrimAnimator != null) {
+            mNormalToHintOverviewScrimAnimator.setCurrentFraction(fraction);
+        }
+    }
+
+    @Override
+    public void onDragEnd(float velocity) {
+        super.onDragEnd(velocity);
+        mNormalToHintOverviewScrimAnimator = null;
+    }
+
+    @Override
     protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
             LauncherState targetState, float velocity, boolean isFling) {
         super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity,
@@ -129,6 +157,7 @@
         if (mCurrentAnimation == null) {
             return;
         }
+        mNormalToHintOverviewScrimAnimator = null;
         mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
             mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
                 mReachedOverview = true;
@@ -144,6 +173,13 @@
         }
     }
 
+    // Used if flinging back to home after reaching overview
+    private void maybeSwipeInteractionToHomeComplete() {
+        if (mIsHomeStaggeredAnimFinished && mIsOverviewRehidden) {
+            onSwipeInteractionCompleted(NORMAL, Touch.FLING);
+        }
+    }
+
     @Override
     protected boolean handlingOverviewAnim() {
         return mDidTouchStartInNavBar && super.handlingOverviewAnim();
@@ -180,9 +216,17 @@
                 stateManager.goToState(NORMAL, true,
                         () -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
             } else {
+                mIsHomeStaggeredAnimFinished = mIsOverviewRehidden = false;
+
                 StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
                         mLauncher, velocity, false /* animateOverviewScrim */);
-                staggeredWorkspaceAnim.start();
+                staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
+                    @Override
+                    public void onAnimationSuccess(Animator animator) {
+                        mIsHomeStaggeredAnimFinished = true;
+                        maybeSwipeInteractionToHomeComplete();
+                    }
+                }).start();
 
                 // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
                 stateManager.cancelAnimation();
@@ -191,8 +235,10 @@
                 config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
                 AnimatorSet anim = stateManager.createAtomicAnimation(
                         stateManager.getState(), NORMAL, config);
-                anim.addListener(AnimationSuccessListener.forRunnable(
-                        () -> onSwipeInteractionCompleted(NORMAL, Touch.SWIPE)));
+                anim.addListener(AnimationSuccessListener.forRunnable(() -> {
+                    mIsOverviewRehidden = true;
+                    maybeSwipeInteractionToHomeComplete();
+                }));
                 anim.start();
             }
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 6751723..edefbe1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -120,9 +120,7 @@
     @Override
     public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
             boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
-        ((RecentsView) getCreatedActivity().getOverviewPanel())
-                .setLayoutRotation(deviceState.getCurrentActiveRotation(),
-                        deviceState.getDisplayRotation());
+        notifyRecentsOfOrientation(deviceState);
         DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
             @Override
             public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 177f9a0..ed07062 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -19,6 +19,7 @@
 import static android.view.Surface.ROTATION_0;
 
 import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -70,10 +71,19 @@
 
         // Add overview actions to the menu when in in-place rotate landscape mode.
         if (!canLauncherRotate && isInLandscape) {
-            for (TaskShortcutFactory actionMenuOption : ACTION_MENU_OPTIONS) {
-                SystemShortcut shortcut = actionMenuOption.getShortcut(activity, taskView);
-                if (shortcut != null) {
-                    shortcuts.add(shortcut);
+            // Add screenshot action to task menu.
+            SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
+                    .getShortcut(activity, taskView);
+            if (screenshotShortcut != null) {
+                shortcuts.add(screenshotShortcut);
+            }
+
+            // Add modal action only if display orientation is the same as the device orientation.
+            if (orientedState.getDisplayRotation() == ROTATION_0) {
+                SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
+                        .getShortcut(activity, taskView);
+                if (modalShortcut != null) {
+                    shortcuts.add(modalShortcut);
                 }
             }
         }
@@ -104,11 +114,6 @@
             TaskShortcutFactory.WELLBEING
     };
 
-    private static final TaskShortcutFactory[] ACTION_MENU_OPTIONS = new TaskShortcutFactory[]{
-        TaskShortcutFactory.SCREENSHOT,
-        TaskShortcutFactory.MODAL
-    };
-
     /**
      * Overlay on each task handling Overview Action Buttons.
      */
@@ -139,8 +144,12 @@
         /**
          * Called when the current task is interactive for the user
          */
-        public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
+        public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
+                boolean rotated) {
             final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+
+            mActionsView.updateDisabledFlags(DISABLED_ROTATED, rotated);
+
             getActionsView().setCallbacks(new OverlayUICallbacks() {
                 @Override
                 public void onShare() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
index 4eae437..ff051b6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -196,7 +196,8 @@
                 WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
                         future, animStartedListener, mHandler, true /* scaleUp */,
                         taskKey.displayId);
-                mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.getItemInfo());
+                mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+                        .log(mLauncherEvent);
             }
         }
     }
@@ -296,8 +297,8 @@
             };
             mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
             dismissTaskMenuView(mTarget);
-            mTarget.getStatsLogManager().log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP,
-                    mTaskView.getItemInfo());
+            mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
+                    .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
         }
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 11fee2f..32b1c58 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -36,8 +36,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
-import java.util.function.Predicate;
-
 /**
  * Input consumer for handling touch on the recents/Launcher activity.
  */
@@ -50,8 +48,6 @@
     private final InputMonitorCompat mInputMonitor;
 
     private final int[] mLocationOnScreen = new int[2];
-    private final boolean mProxyTouch;
-    private final Predicate<MotionEvent> mEventReceiver;
 
     private final boolean mStartingInActivityBounds;
     private boolean mTargetHandledTouch;
@@ -64,15 +60,7 @@
         mActivityInterface = gestureState.getActivityInterface();
 
         mTarget = activity.getDragLayer();
-        if (startingInActivityBounds) {
-            mEventReceiver = mTarget::dispatchTouchEvent;
-            mProxyTouch = true;
-        } else {
-            // Only proxy touches to controllers if we are starting touch from nav bar.
-            mEventReceiver = mTarget::proxyTouchEvent;
-            mTarget.getLocationOnScreen(mLocationOnScreen);
-            mProxyTouch = mTarget.prepareProxyEventStarting();
-        }
+        mTarget.getLocationOnScreen(mLocationOnScreen);
     }
 
     @Override
@@ -87,10 +75,6 @@
 
     @Override
     public void onMotionEvent(MotionEvent ev) {
-        if (!mProxyTouch) {
-            return;
-        }
-
         int flags = ev.getEdgeFlags();
         if (!mStartingInActivityBounds) {
             ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
@@ -99,7 +83,7 @@
         if (TestProtocol.sDebugTracing) {
             Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
         }
-        boolean handled = mEventReceiver.test(ev);
+        boolean handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
         ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
         ev.setEdgeFlags(flags);
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 8daa982..3cafd42 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -175,6 +175,11 @@
         return mAnimators;
     }
 
+    public StaggeredWorkspaceAnim addAnimatorListener(Animator.AnimatorListener listener) {
+        mAnimators.addListener(listener);
+        return this;
+    }
+
     /**
      * Starts the animation.
      */
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 196a7c4..70fb78e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -141,7 +141,8 @@
      */
     public void setPreview(RemoteAnimationTargetCompat runningTarget) {
         setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
-        mRunningTargetWindowPosition.set(runningTarget.position.x, runningTarget.position.y);
+        mRunningTargetWindowPosition.set(runningTarget.screenSpaceBounds.left,
+                runningTarget.screenSpaceBounds.top);
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
index 6b99f90..0979c07 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
@@ -232,7 +232,7 @@
                 launcher, parent);
         view.init(launcher);
         launcher.getDragLayer().addView(view);
-        launcher.getStatsLogManager().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
+        launcher.getStatsLogManager().logger().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
 
         view.requestLayout();
         view.playAnimation();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index 95eb10f..a2da398 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -24,7 +24,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.FrameLayout;
@@ -39,6 +38,7 @@
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
+import com.android.quickstep.util.LayoutUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -68,6 +68,15 @@
     public static final int HIDDEN_GESTURE_RUNNING = 1 << 4;
     public static final int HIDDEN_NO_RECENTS = 1 << 5;
 
+    @IntDef(flag = true, value = {
+            DISABLED_SCROLLING,
+            DISABLED_ROTATED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActionsDisabledFlags { }
+
+    public static final int DISABLED_SCROLLING = 1 << 0;
+    public static final int DISABLED_ROTATED = 1 << 1;
+
     private static final int INDEX_CONTENT_ALPHA = 0;
     private static final int INDEX_VISIBILITY_ALPHA = 1;
     private static final int INDEX_FULLSCREEN_ALPHA = 2;
@@ -78,6 +87,9 @@
     @ActionsHiddenFlags
     private int mHiddenFlags;
 
+    @ActionsDisabledFlags
+    protected int mDisabledFlags;
+
     protected T mCallbacks;
 
     public OverviewActionsView(Context context) {
@@ -117,7 +129,6 @@
     @Override
     public void onClick(View view) {
         if (mCallbacks == null) {
-            Log.d("OverviewActionsView", "Callbacks null onClick");
             return;
         }
         int id = view.getId();
@@ -158,6 +169,25 @@
         setVisibility(isHidden ? INVISIBLE : VISIBLE);
     }
 
+    /**
+     * Updates the proper disabled flag to indicate whether OverviewActionsView should be enabled.
+     * Ignores DISABLED_ROTATED flag for determining enabled. Flag is used to enable/disable
+     * buttons individually, currently done for select button in subclass.
+     *
+     * @param disabledFlags The flag to update.
+     * @param enable        Whether to enable the disable flag: True will cause view to be disabled.
+     */
+    public void updateDisabledFlags(@ActionsDisabledFlags int disabledFlags, boolean enable) {
+        if (enable) {
+            mDisabledFlags |= disabledFlags;
+        } else {
+            mDisabledFlags &= ~disabledFlags;
+        }
+        //
+        boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
+        LayoutUtils.setViewEnabled(this, isEnabled);
+    }
+
     public AlphaProperty getContentAlpha() {
         return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index fc50845..8604c6c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -208,7 +208,7 @@
                 }
             };
 
-    protected final RecentsOrientedState mOrientationState;
+    protected RecentsOrientedState mOrientationState;
     protected final BaseActivityInterface mSizeStrategy;
     protected RecentsAnimationController mRecentsAnimationController;
     protected RecentsAnimationTargets mRecentsAnimationTargets;
@@ -371,11 +371,17 @@
 
     private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
             (inMultiWindowMode) -> {
-        if (!inMultiWindowMode && mOverviewStateEnabled) {
-            // TODO: Re-enable layout transitions for addition of the unpinned task
-            reloadIfNeeded();
-        }
-    };
+                if (mOrientationState != null) {
+                    mOrientationState.setMultiWindowMode(inMultiWindowMode);
+                    setLayoutRotation(mOrientationState.getTouchRotation(),
+                            mOrientationState.getDisplayRotation());
+                    rotateAllChildTasks();
+                }
+                if (!inMultiWindowMode && mOverviewStateEnabled) {
+                    // TODO: Re-enable layout transitions for addition of the unpinned task
+                    reloadIfNeeded();
+                }
+            };
 
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
             BaseActivityInterface sizeStrategy) {
@@ -488,7 +494,8 @@
             return;
         }
         mModel.getIconCache().clear();
-        reset();
+        unloadVisibleTaskData();
+        loadVisibleTaskData();
     }
 
     public void init(OverviewActionsView actionsView) {
@@ -622,14 +629,14 @@
     @Override
     protected void onPageBeginTransition() {
         super.onPageBeginTransition();
-        LayoutUtils.setViewEnabled(mActionsView, false);
+        mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
     }
 
     @Override
     protected void onPageEndTransition() {
         super.onPageEndTransition();
         if (isClearAllHidden()) {
-            LayoutUtils.setViewEnabled(mActionsView, true);
+            mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
         }
         if (getNextPage() > 0) {
             setSwipeDownShouldLaunchApp(true);
@@ -842,7 +849,6 @@
 
     private void resetPaddingFromTaskSize() {
         DeviceProfile dp = mActivity.getDeviceProfile();
-        mOrientationState.setMultiWindowMode(dp.isMultiWindowMode);
         getTaskSize(mTempRect);
         mTaskWidth = mTempRect.width();
         mTaskHeight = mTempRect.height();
@@ -1055,8 +1061,7 @@
 
     private void animateRecentsRotationInPlace(int newRotation) {
         if (mOrientationState.canLauncherRotate()) {
-            // Update the rotation but let system take care of the rotation animation
-            setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
+            // Let system take care of the rotation
             return;
         }
         AnimatorSet pa = setRecentsChangedOrientation(true);
@@ -1349,8 +1354,8 @@
             ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     endState.logAction, Direction.UP, index, compKey);
-            mActivity.getStatsLogManager().log(
-                    LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.getItemInfo());
+            mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
+                    .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
         }
     }
 
@@ -1641,7 +1646,7 @@
                     : View.LAYOUT_DIRECTION_RTL);
             mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
             mActivity.getDragLayer().recreateControllers();
-            boolean isInLandscape = touchRotation != 0
+            boolean isInLandscape = mOrientationState.getTouchRotation() != 0
                     || mOrientationState.getLauncherRotation() != ROTATION_0;
             mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
                     !mOrientationState.canLauncherRotate() && isInLandscape);
@@ -1730,6 +1735,8 @@
         setPivotY(mTempPointF.y);
         setTaskModalness(mTaskModalness);
         updatePageOffsets();
+        setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
+                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
 
     private void updatePageOffsets() {
@@ -1942,8 +1949,8 @@
                     mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                             endState.logAction, Direction.DOWN, indexOfChild(tv),
                             TaskUtils.getLaunchComponentKeyForTask(task.key));
-                    mActivity.getStatsLogManager().log(
-                            LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.getItemInfo());
+                    mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
+                            .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
                 }
             } else {
                 onTaskLaunched(false);
@@ -2113,6 +2120,12 @@
         return mClearAllButton;
     }
 
+    @Override
+    protected boolean onOverscroll(int amount) {
+        // overscroll should only be accepted on -1 direction (for clear all button)
+        if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
+        return super.onOverscroll(amount);
+    }
 
     /**
      * @return How many pixels the running task is offset on the currently laid out dominant axis.
@@ -2210,7 +2223,6 @@
         boolean inPlaceLandscape = !mOrientationState.canLauncherRotate()
                 && mOrientationState.getTouchRotation() != ROTATION_0;
         mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
-        LayoutUtils.setViewEnabled(mActionsView, true);
     }
 
     @Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 26fb563..b2f937f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -357,10 +357,9 @@
     }
 
     private void updateOverlay() {
-        // The overlay doesn't really work when the screenshot is rotated, so don't add it.
-        if (mOverlayEnabled && !mPreviewPositionHelper.mIsOrientationChanged
-                && mBitmapShader != null && mThumbnailData != null) {
-            mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix);
+        if (mOverlayEnabled && mBitmapShader != null && mThumbnailData != null) {
+            mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
+                    mPreviewPositionHelper.mIsOrientationChanged);
         } else {
             mOverlay.reset();
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index be2e234..8bdf99e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -214,7 +214,8 @@
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-            mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, getItemInfo());
+            mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
+                    .log(LAUNCHER_TASK_LAUNCH_TAP);
         });
 
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -432,7 +433,8 @@
             getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
         } else {
             mMenuView = TaskMenuView.showForTask(this);
-            mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, getItemInfo());
+            mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
+                    .log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
             UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
                     LauncherLogProto.ItemType.TASK_ICON);
             if (mMenuView != null) {
@@ -969,6 +971,9 @@
     }
 
     void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
+        if (getRecentsView() == null) {
+            return;
+        }
         mCurrentFullscreenParams.setProgress(
                 mFullscreenProgress,
                 getRecentsView().getScaleX(),
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 2b11ca0..0f2955b 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -17,7 +17,9 @@
     <string name="task_overlay_factory_class" translatable="false"/>
 
     <!-- Activities which block home gesture -->
-    <string-array name="gesture_blocking_activities" translatable="false"/>
+    <string-array name="gesture_blocking_activities" translatable="false">
+        <item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
+    </string-array>
 
     <string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
 
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 1b82826..769d298 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -87,7 +87,7 @@
     <string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
     <!-- tip shown when user migrates and predictions are enabled in hotseat -->
     <string name="hotsaet_tip_prediction_enabled">App suggestions enabled</string>
-    <!-- tip shown when hotseat edu is requested while predicions are disabled -->
+    <!-- tip shown when hotseat edu is requested while predictions are disabled -->
     <string name="hotsaet_tip_prediction_disabled">App suggestions are disabled</string>
 
     <!-- content description for hotseat items -->
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 8745814..20ee61d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -16,6 +16,8 @@
 package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -36,7 +38,6 @@
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
@@ -124,7 +125,7 @@
                 return false;
             }
         }
-        if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE) != null) {
+        if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {
             return false;
         }
         return true;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index ebb44e2..e496807 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.PREDICTED_HOTSEAT_CONTAINER;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
@@ -30,9 +31,9 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
@@ -42,11 +43,14 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.LogConfig;
 import com.android.systemui.shared.system.SysUiStatsLog;
 
 import java.util.ArrayList;
+import java.util.Optional;
+import java.util.OptionalInt;
 
 /**
  * This class calls StatsLog compile time generated methods.
@@ -73,56 +77,9 @@
         sContext = context;
     }
 
-    /**
-     * Logs a {@link EventEnum}.
-     */
     @Override
-    public void log(EventEnum event) {
-        log(event, DEFAULT_INSTANCE_ID, null);
-    }
-
-    /**
-     * Logs an event and accompanying {@link InstanceId}.
-     */
-    @Override
-    public void log(EventEnum event, InstanceId instanceId) {
-        log(event, instanceId, null);
-    }
-
-    /**
-     * Logs an event and accompanying {@link ItemInfo}.
-     */
-    @Override
-    public void log(EventEnum event, @Nullable ItemInfo info) {
-        log(event, DEFAULT_INSTANCE_ID, info);
-    }
-
-    /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param atomItemInfo item typically containing app or task launch related information.
-     */
-    @Override
-    public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo atomItemInfo, int srcState,
-            int dstState) {
-        write(event, DEFAULT_INSTANCE_ID,
-                atomItemInfo == null ? LauncherAtom.ItemInfo.getDefaultInstance() : atomItemInfo,
-                null,
-                srcState,
-                dstState);
-    }
-
-    /**
-     * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
-     */
-    @Override
-    public void log(EventEnum event, InstanceId instanceId,
-            @Nullable ItemInfo info) {
-        logInternal(event, instanceId, info,
-                LAUNCHER_UICHANGED__DST_STATE__HOME,
-                LAUNCHER_UICHANGED__DST_STATE__BACKGROUND,
-                DEFAULT_PAGE_INDEX);
+    public StatsLogger logger() {
+        return new StatsCompatLogger();
     }
 
     /**
@@ -139,102 +96,6 @@
     }
 
     /**
-     * Logs an event and accompanying {@link LauncherState}s. If either of the state refers
-     * to workspace state, then use pageIndex to pass in index of workspace.
-     */
-    @Override
-    public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
-        logInternal(event, DEFAULT_INSTANCE_ID, null, srcState, dstState, pageIndex);
-    }
-
-    /**
-     * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
-     */
-    private void logInternal(EventEnum event, InstanceId instanceId,
-            @Nullable ItemInfo info, int srcState, int dstState, int pageIndex) {
-
-        LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
-                new BaseModelUpdateTask() {
-                    @Override
-                    public void execute(LauncherAppState app, BgDataModel dataModel,
-                            AllAppsList apps) {
-                        writeEvent(event, instanceId, info, srcState, dstState, pageIndex,
-                                dataModel.folders);
-                    }
-                });
-    }
-
-    private static void writeEvent(EventEnum event, InstanceId instanceId,
-            @Nullable ItemInfo info, int srcState, int dstState, int pageIndex,
-            IntSparseArrayMap<FolderInfo> folders) {
-
-        if (!Utilities.ATLEAST_R) {
-            return;
-        }
-        LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
-        if (info != null) {
-            if (info.container >= 0) {
-                atomInfo = info.buildProto(folders.get(info.container));
-            } else {
-                atomInfo = info.buildProto();
-            }
-        } else {
-            if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
-                    || dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
-                atomInfo = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
-                        LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
-                                LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
-                        )).build();
-            }
-        }
-        write(event, instanceId, atomInfo, info, srcState, dstState);
-    }
-
-    private static void write(EventEnum event, InstanceId instanceId,
-            LauncherAtom.ItemInfo atomInfo,
-            @Nullable ItemInfo info,
-            int srcState, int dstState) {
-        if (IS_VERBOSE) {
-            String name = (event instanceof Enum) ? ((Enum) event).name() :
-                    event.getId() + "";
-
-            Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
-                    ? String.format("\n%s (State:%s->%s) \n%s\n%s", name, getStateString(srcState),
-                            getStateString(dstState), info, atomInfo)
-                    : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s\n%s", name,
-                            getStateString(srcState), getStateString(dstState), instanceId, info,
-                            atomInfo));
-        }
-
-        SysUiStatsLog.write(
-                SysUiStatsLog.LAUNCHER_EVENT,
-                SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
-                srcState,
-                dstState,
-                null /* launcher extensions, deprecated */,
-                false /* quickstep_enabled, deprecated */,
-                event.getId() /* event_id */,
-                atomInfo.getItemCase().getNumber() /* target_id */,
-                instanceId.getId() /* instance_id TODO */,
-                0 /* uid TODO */,
-                getPackageName(atomInfo) /* package_name */,
-                getComponentName(atomInfo) /* component_name */,
-                getGridX(atomInfo, false) /* grid_x */,
-                getGridY(atomInfo, false) /* grid_y */,
-                getPageId(atomInfo, false) /* page_id */,
-                getGridX(atomInfo, true) /* grid_x_parent */,
-                getGridY(atomInfo, true) /* grid_y_parent */,
-                getPageId(atomInfo, true) /* page_id_parent */,
-                getHierarchy(atomInfo) /* hierarchy */,
-                atomInfo.getIsWork() /* is_work_profile */,
-                atomInfo.getRank() /* rank */,
-                atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
-                atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
-                atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
-                getCardinality(atomInfo) /* cardinality */);
-    }
-
-    /**
      * Logs the workspace layout information on the model thread.
      */
     @Override
@@ -260,7 +121,8 @@
                 writeSnapshot(atomInfo, mInstanceId);
             }
             for (FolderInfo fInfo : folders) {
-                for (ItemInfo info : fInfo.contents) {
+                ArrayList<WorkspaceItemInfo> folderContents = (ArrayList) fInfo.contents.clone();
+                for (ItemInfo info : folderContents) {
                     LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
                     writeSnapshot(atomInfo, mInstanceId);
                 }
@@ -280,7 +142,7 @@
             return;
         }
         SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
-                0 /* event_id */,
+                LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
                 info.getItemCase().getNumber() /* target_id */,
                 instanceId.getId() /* instance_id */,
                 0 /* uid */,
@@ -300,6 +162,146 @@
                 info.getWidget().getSpanY());
     }
 
+    /**
+     * Helps to construct and write statsd compatible log message.
+     */
+    private static class StatsCompatLogger implements StatsLogger {
+
+        private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
+        private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
+        private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
+        private OptionalInt mRank = OptionalInt.empty();
+        private Optional<ContainerInfo> mContainerInfo = Optional.empty();
+        private int mSrcState = LAUNCHER_UICHANGED__SRC_STATE__HOME;
+        private int mDstState = LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
+
+        @Override
+        public StatsLogger withItemInfo(ItemInfo itemInfo) {
+            if (mContainerInfo.isPresent()) {
+                throw new IllegalArgumentException(
+                        "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
+            }
+            this.mItemInfo = itemInfo;
+            return this;
+        }
+
+        @Override
+        public StatsLogger withInstanceId(InstanceId instanceId) {
+            this.mInstanceId = instanceId;
+            return this;
+        }
+
+        @Override
+        public StatsLogger withRank(int rank) {
+            this.mRank = OptionalInt.of(rank);
+            return this;
+        }
+
+        @Override
+        public StatsLogger withSrcState(int srcState) {
+            this.mSrcState = srcState;
+            return this;
+        }
+
+        @Override
+        public StatsLogger withDstState(int dstState) {
+            this.mDstState = dstState;
+            return this;
+        }
+
+        @Override
+        public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
+            if (mItemInfo != DEFAULT_ITEM_INFO) {
+                throw new IllegalArgumentException(
+                        "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
+            }
+            this.mContainerInfo = Optional.of(containerInfo);
+            return this;
+        }
+
+        @Override
+        public void log(EventEnum event) {
+            if (!Utilities.ATLEAST_R) {
+                return;
+            }
+
+            LauncherAtom.ItemInfo.Builder itemInfoBuilder =
+                    (LauncherAtom.ItemInfo.Builder) mItemInfo.buildProto().toBuilder();
+            mRank.ifPresent(itemInfoBuilder::setRank);
+            if (mContainerInfo.isPresent()) {
+                // User already provided container info;
+                // default container info from item info will be ignored.
+                itemInfoBuilder.setContainerInfo(mContainerInfo.get());
+                write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
+                return;
+            }
+
+            if (mItemInfo.container < 0) {
+                // Item is not within a folder. Write to StatsLog in same thread.
+                write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
+            } else {
+                // Item is inside the folder, fetch folder info in a BG thread
+                // and then write to StatsLog.
+                LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+                        new BaseModelUpdateTask() {
+                            @Override
+                            public void execute(LauncherAppState app, BgDataModel dataModel,
+                                    AllAppsList apps) {
+                                FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
+                                LauncherAtom.ItemInfo.Builder atomInfoBuilder =
+                                        (LauncherAtom.ItemInfo.Builder) mItemInfo
+                                                .buildProto(folderInfo).toBuilder();
+                                mRank.ifPresent(atomInfoBuilder::setRank);
+                                write(event, mInstanceId, atomInfoBuilder.build(), mSrcState,
+                                        mDstState);
+                            }
+                        });
+            }
+        }
+
+        private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
+                int srcState, int dstState) {
+            if (IS_VERBOSE) {
+                String name = (event instanceof Enum) ? ((Enum) event).name() :
+                        event.getId() + "";
+
+                Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
+                        ? String.format("\n%s (State:%s->%s)\n%s", name, getStateString(srcState),
+                        getStateString(dstState), atomInfo)
+                        : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
+                                getStateString(srcState), getStateString(dstState), instanceId,
+                                atomInfo));
+            }
+
+            SysUiStatsLog.write(
+                    SysUiStatsLog.LAUNCHER_EVENT,
+                    SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
+                    srcState,
+                    dstState,
+                    null /* launcher extensions, deprecated */,
+                    false /* quickstep_enabled, deprecated */,
+                    event.getId() /* event_id */,
+                    atomInfo.getItemCase().getNumber() /* target_id */,
+                    instanceId.getId() /* instance_id TODO */,
+                    0 /* uid TODO */,
+                    getPackageName(atomInfo) /* package_name */,
+                    getComponentName(atomInfo) /* component_name */,
+                    getGridX(atomInfo, false) /* grid_x */,
+                    getGridY(atomInfo, false) /* grid_y */,
+                    getPageId(atomInfo, false) /* page_id */,
+                    getGridX(atomInfo, true) /* grid_x_parent */,
+                    getGridY(atomInfo, true) /* grid_y_parent */,
+                    getPageId(atomInfo, true) /* page_id_parent */,
+                    getHierarchy(atomInfo) /* hierarchy */,
+                    atomInfo.getIsWork() /* is_work_profile */,
+                    atomInfo.getRank() /* rank */,
+                    atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
+                    atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
+                    atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
+                    getCardinality(atomInfo) /* cardinality */);
+        }
+    }
+
     private static int getCardinality(LauncherAtom.ItemInfo info) {
         return info.getContainerInfo().getContainerCase().equals(PREDICTED_HOTSEAT_CONTAINER)
                 ? info.getContainerInfo().getPredictedHotseatContainer().getCardinality()
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 3f58e01..cefab1b 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -74,17 +74,14 @@
 
     /**
      * Recursively sets view and all children enabled/disabled.
-     * @param viewGroup Top most parent view to change.
+     * @param view Top most parent view to change.
      * @param enabled True = enable, False = disable.
      */
-    public static void setViewEnabled(ViewGroup viewGroup, boolean enabled) {
-        viewGroup.setEnabled(enabled);
-        for (int i = 0; i < viewGroup.getChildCount(); i++) {
-            View child = viewGroup.getChildAt(i);
-            if (child instanceof ViewGroup) {
-                setViewEnabled((ViewGroup) child, enabled);
-            } else {
-                child.setEnabled(enabled);
+    public static void setViewEnabled(View view, boolean enabled) {
+        view.setEnabled(enabled);
+        if (view instanceof ViewGroup) {
+            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
+                setViewEnabled(((ViewGroup) view).getChildAt(i), enabled);
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 1abe903..0fdc684 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -115,7 +115,7 @@
                     HotseatPredictionController client = mLauncher.getHotseatPredictionController();
                     if (mFromAllApps && finalState == NORMAL && client.hasPredictions()) {
                         if (incrementEventCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
-                            client.showDiscoveryTip();
+                            client.showEdu();
                             stateManager.removeStateListener(this);
                         }
                     }
@@ -144,7 +144,7 @@
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
                     if (finalState == NORMAL) {
-                        if (mCount == MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
+                        if (mCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
                             if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
                                 AllAppsEduView.show(launcher);
                             }
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 8bd2281..68636cb 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -116,14 +116,10 @@
             MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
                     | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
 
-    private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
-            newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
-
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final SharedPreferences mSharedPrefs;
     private final OrientationEventListener mOrientationListener;
-    private final BaseActivityInterface mSizeStrategy;
 
     private final Matrix mTmpMatrix = new Matrix();
 
@@ -140,7 +136,6 @@
         mContext = context;
         mContentResolver = context.getContentResolver();
         mSharedPrefs = Utilities.getPrefs(context);
-        mSizeStrategy = sizeStrategy;
         mOrientationListener = new OrientationEventListener(context) {
             @Override
             public void onOrientationChanged(int degrees) {
@@ -268,10 +263,9 @@
 
     private void initFlags() {
         SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
-        if (mOrientationListener.canDetectOrientation() &&
-                currentMode != TWO_BUTTONS) {
-            mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
-        }
+        boolean rotationWatcherSupported = mOrientationListener.canDetectOrientation() &&
+                currentMode != TWO_BUTTONS;
+        setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, rotationWatcherSupported);
 
         // initialize external flags
         updateAutoRotateSetting();
@@ -288,9 +282,6 @@
             mContentResolver.registerContentObserver(
                     Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
                     false, mSystemAutoRotateObserver);
-            SysUINavigationMode.Mode currentMode =
-                    SysUINavigationMode.INSTANCE.get(mContext)
-                            .addModeChangeListener(mNavModeChangeListener);
         }
         initFlags();
     }
@@ -302,8 +293,6 @@
         if (isMultipleOrientationSupportedByDevice()) {
             mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
             mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
-            SysUINavigationMode.INSTANCE.get(mContext)
-                    .removeModeChangeListener(mNavModeChangeListener);
         }
         setRotationWatcherEnabled(false);
     }
@@ -349,20 +338,6 @@
         setFlag(FLAG_ROTATION_WATCHER_ENABLED, isEnabled);
     }
 
-    public int getTouchRotationDegrees() {
-        switch (mTouchRotation) {
-            case ROTATION_90:
-                return 90;
-            case ROTATION_180:
-                return 180;
-            case ROTATION_270:
-                return 270;
-            case ROTATION_0:
-            default:
-                return 0;
-        }
-    }
-
     /**
      * Returns the scale and pivot so that the provided taskRect can fit the provided full size
      */
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index bec3050..19e278b 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -244,13 +244,17 @@
 
     @Override
     protected void updateSysUiColors() {
-        // Use a light system UI (dark icons) if all apps is behind at least half of the
-        // status bar.
-        boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
-        if (forceChange) {
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
+        if (mDrawingFlatColor) {
+            super.updateSysUiColors();
         } else {
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
+            // Use a light system UI (dark icons) if all apps is behind at least half of the
+            // status bar.
+            boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
+            if (forceChange) {
+                mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
+            } else {
+                mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
+            }
         }
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index a343e7c..bd8ab08 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -120,6 +120,9 @@
                     TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
                     UiDevice.getInstance(getInstrumentation()).executeShellCommand(
                             getLauncherCommand(getLauncherInMyProcess()));
+                    // b/143488140
+                    mDevice.pressHome();
+                    mDevice.waitForIdle();
                 }
             }
         };
diff --git a/res/values/config.xml b/res/values/config.xml
index 4cbc597..ca25325 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -144,6 +144,10 @@
     <item name="staggered_stiffness" type="dimen" format="float">150</item>
     <dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
 
+    <item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
+    <item name="hint_scale_stiffness" type="dimen" format="float">200</item>
+    <dimen name="hint_scale_velocity_dp_per_s">0.3dp</dimen>
+
     <!-- Swipe up to home related -->
     <dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
     <dimen name="swipe_up_y_overshoot">10dp</dimen>
@@ -175,6 +179,10 @@
 
         <item>@dimen/swipe_up_fling_min_visible_change</item>
         <item>@dimen/swipe_up_y_overshoot</item>
+
+        <item>@dimen/hint_scale_damping_ratio</item>
+        <item>@dimen/hint_scale_stiffness</item>
+        <item>@dimen/hint_scale_velocity_dp_per_s</item>
     </array>
 
     <string-array name="live_wallpapers_remove_sysui_scrims">
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a922183..ac00488 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,7 +50,7 @@
         <item name="folderFillColor">#CDFFFFFF</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="folderTextColor">#FF212121</item>
-        <item name="folderHintColor">#FF616161</item>
+        <item name="folderHintColor">#89616161</item>
         <item name="loadingIconColor">#CCFFFFFF</item>
         <item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
         <item name="workProfileOverlayTextColor">#FF212121</item>
@@ -106,7 +106,7 @@
         <item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
         <item name="folderIconBorderColor">#FF80868B</item>
         <item name="folderTextColor">@android:color/white</item>
-        <item name="folderHintColor">#FFCCCCCC</item>
+        <item name="folderHintColor">#89CCCCCC</item>
         <item name="isMainColorDark">true</item>
         <item name="loadingIconColor">#99FFFFFF</item>
         <item name="iconOnlyShortcutColor">#B3FFFFFF</item>
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 88dbfd6..9cb8cf2 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -43,6 +43,8 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -188,7 +190,8 @@
             }
             getUserEventDispatcher().logAppLaunch(v, intent, user);
             if (item != null) {
-                getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item);
+                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+                logAppLaunch(item, instanceId);
             }
             return true;
         } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
@@ -198,6 +201,11 @@
         return false;
     }
 
+    protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+        getStatsLogManager().logger().withItemInfo(info).withInstanceId(instanceId)
+                .log(LAUNCHER_APP_LAUNCH_TAP);
+    }
+
     private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
             @Nullable String sourceContainer) {
         try {
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index d75d712..2857497 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -126,11 +126,9 @@
             d.dragInfo.container = NO_ID;
         }
         super.onDrop(d, options);
-        mStatsLogManager.log(
-                mControlType == ControlType.REMOVE_TARGET
-                        ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
-                        : LAUNCHER_ITEM_DROPPED_ON_CANCEL,
-                d.logInstanceId);
+        mStatsLogManager.logger().withInstanceId(d.logInstanceId)
+                .log(mControlType == ControlType.REMOVE_TARGET ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+                        : LAUNCHER_ITEM_DROPPED_ON_CANCEL);
     }
 
     @Override
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 60abc66..e39e89c 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -148,6 +148,7 @@
         iconSize = p.iconSize;
         iconShapePath = p.iconShapePath;
         landscapeIconSize = p.landscapeIconSize;
+        iconBitmapSize = p.iconBitmapSize;
         iconTextSize = p.iconTextSize;
         numHotseatIcons = p.numHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index fbac0bd..499b54f 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -220,9 +220,11 @@
         d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
         super.onDrop(d, options);
         if (mCurrentAccessibilityAction == UNINSTALL) {
-            mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL, d.logInstanceId);
+            mStatsLogManager.logger().withInstanceId(d.logInstanceId)
+                    .log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
         } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
-            mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST, d.logInstanceId);
+            mStatsLogManager.logger().withInstanceId(d.logInstanceId)
+                    .log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
         }
     }
 
@@ -348,10 +350,12 @@
                     mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
                 mDragObject.dragSource = mOriginal;
                 mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
-                mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED, mDragObject.logInstanceId);
+                mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
+                        .log(LAUNCHER_ITEM_UNINSTALL_COMPLETED);
             } else {
                 sendFailure();
-                mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED, mDragObject.logInstanceId);
+                mStatsLogManager.logger().withInstanceId(mDragObject.logInstanceId)
+                        .log(LAUNCHER_ITEM_UNINSTALL_CANCELLED);
             }
         }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4198e9f..fb58f21 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -415,11 +415,9 @@
 
         // Always enter the spring loaded mode
         mLauncher.getStateManager().goToState(SPRING_LOADED);
-        mStatsLogManager.log(
-                LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
-                dragObject.logInstanceId,
-                dragObject.originalDragInfo
-        );
+        mStatsLogManager.logger().withItemInfo(dragObject.originalDragInfo)
+                .withInstanceId(dragObject.logInstanceId)
+                .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
     }
 
     public void deferRemoveExtraEmptyScreen() {
@@ -1646,10 +1644,8 @@
             Rect folderLocation = new Rect();
             float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
             target.removeView(v);
-            mStatsLogManager.log(
-                    LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
-                    d.logInstanceId,
-                    destInfo);
+            mStatsLogManager.logger().withItemInfo(destInfo).withInstanceId(d.logInstanceId)
+                    .log(LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED);
             FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
                     targetCell[1]);
             destInfo.cellX = -1;
@@ -1687,10 +1683,8 @@
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
             if (fi.acceptDrop(d.dragInfo)) {
-                mStatsLogManager.log(
-                        LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
-                        d.logInstanceId,
-                        fi.mInfo);
+                mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId)
+                        .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
                 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
 
                 // if the drag started here, we need to remove it from the workspace
@@ -1893,10 +1887,8 @@
 
             mLauncher.getStateManager().goToState(
                     NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
-            mStatsLogManager.log(
-                    LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
-                    d.logInstanceId,
-                    d.dragInfo);
+            mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
+                    .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
 
         if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@@ -2434,10 +2426,9 @@
                     // widgets/shortcuts/folders in a slightly different way
                     mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
                             item.spanX, item.spanY);
-                    mStatsLogManager.log(
-                            LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
-                            d.logInstanceId,
-                            d.dragInfo);
+                    mStatsLogManager.logger().withItemInfo(d.dragInfo)
+                            .withInstanceId(d.logInstanceId)
+                            .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
                 }
             };
             boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -2526,10 +2517,8 @@
                 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
                 resetTransitionTransform();
             }
-            mStatsLogManager.log(
-                    LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
-                    d.logInstanceId,
-                    d.dragInfo);
+            mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
+                    .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
 
     }
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 06a73db..cd938e1 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE;
+
 import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
@@ -23,11 +25,13 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
 import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_HAS_BACKGROUNDS;
+import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS;
+import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
 import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
@@ -35,6 +39,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 
+import android.animation.ValueAnimator;
 import android.view.View;
 import android.view.animation.Interpolator;
 
@@ -43,8 +48,11 @@
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DynamicResource;
+import com.android.systemui.plugins.ResourceProvider;
 
 /**
  * Manages the animations between each of the workspace states.
@@ -104,17 +112,32 @@
         View qsbView = qsbScaleView.getSearchView();
         if (playAtomicComponent) {
             Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
-            propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
+            LauncherState fromState = mLauncher.getStateManager().getState();
+            boolean shouldSpring = propertySetter instanceof PendingAnimation
+                    && fromState == HINT_STATE && state == NORMAL;
+            if (shouldSpring) {
+                ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
+                        mWorkspace, mNewScale));
+            } else {
+                propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
+            }
 
             setPivotToScaleWithWorkspace(hotseat);
             setPivotToScaleWithWorkspace(qsbScaleView);
             float hotseatScale = hotseatScaleAndTranslation.scale;
-            Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
-                    scaleInterpolator);
-            propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
-                    hotseatScaleInterpolator);
-            propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
-                    hotseatScaleInterpolator);
+            if (shouldSpring) {
+                PendingAnimation pa = (PendingAnimation) propertySetter;
+                pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
+                pa.add(getSpringScaleAnimator(mLauncher, qsbScaleView,
+                        qsbScaleAndTranslation.scale));
+            } else {
+                Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
+                        scaleInterpolator);
+                propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
+                        hotseatScaleInterpolator);
+                propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
+                        hotseatScaleInterpolator);
+            }
 
             float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
             propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
@@ -191,4 +214,24 @@
                     pageAlpha, fadeInterpolator);
         }
     }
+
+    /**
+     * Returns a spring based animator for the scale property of {@param v}.
+     */
+    public static ValueAnimator getSpringScaleAnimator(Launcher launcher, View v, float scale) {
+        ResourceProvider rp = DynamicResource.provider(launcher);
+        float damping = rp.getFloat(R.dimen.hint_scale_damping_ratio);
+        float stiffness = rp.getFloat(R.dimen.hint_scale_stiffness);
+        float velocityPxPerS = rp.getDimension(R.dimen.hint_scale_velocity_dp_per_s);
+
+        return new SpringAnimationBuilder(v.getContext())
+                .setStiffness(stiffness)
+                .setDampingRatio(damping)
+                .setMinimumVisibleChange(MIN_VISIBLE_CHANGE_SCALE)
+                .setEndValue(scale)
+                .setStartValue(SCALE_PROPERTY.get(v))
+                .setStartVelocity(velocityPxPerS)
+                .build(v, SCALE_PROPERTY);
+
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 1dd81e8..a9b030e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -159,7 +159,9 @@
             StateAnimationConfig config, PendingAnimation builder) {
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
-            setAlphas(toState, config, builder);
+            if (!config.onlyPlayAtomicComponent()) {
+                setAlphas(toState, config, builder);
+            }
             // Fail fast
             onProgressAnimationEnd();
             return;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8df6f90..4f53d45 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -126,7 +126,7 @@
     public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
             "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
 
-    public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag(
+    public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
             "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
 
     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 530010e..7998c2d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1337,8 +1337,8 @@
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
-        mStatsLogManager
-                .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo);
+        mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
+                .log(LAUNCHER_ITEM_DROP_COMPLETED);
     }
 
     // This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1443,7 +1443,7 @@
             if (hasFocus) {
                 startEditingFolderName();
             } else {
-                mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo);
+                mStatsLogManager.logger().withItemInfo(mInfo).log(LAUNCHER_FOLDER_LABEL_UPDATED);
                 logFolderLabelState();
                 mFolderName.dispatchBackKey();
             }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 2be5883..b40b1e2 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -448,8 +448,8 @@
             return;
         }
         mInfo.setTitle(nameInfos[0].getLabel());
-        StatsLogManager.newInstance(getContext())
-                .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo);
+        StatsLogManager.newInstance(getContext()).logger().withItemInfo(mInfo)
+                .withInstanceId(instanceId).log(LAUNCHER_FOLDER_LABEL_UPDATED);
         onTitleChanged(mInfo.title);
         mFolder.mFolderName.setText(mInfo.title);
         mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
diff --git a/src/com/android/launcher3/folder/FolderNameEditText.java b/src/com/android/launcher3/folder/FolderNameEditText.java
index edf2c70..6038a05 100644
--- a/src/com/android/launcher3/folder/FolderNameEditText.java
+++ b/src/com/android/launcher3/folder/FolderNameEditText.java
@@ -70,8 +70,11 @@
         for (int i = 0; i < cnt; i++) {
             cInfo[i] = new CompletionInfo(i, i, suggestList.get(i));
         }
-        post(() -> getContext().getSystemService(InputMethodManager.class)
-                .displayCompletions(this, cInfo));
+        // post it to future frame so that onSelectionChanged, onFocusChanged, all other
+        // TextView flag change and IME animation has settled. Ideally, there should be IMM
+        // callback to notify when the IME animation and state handling is finished.
+        postDelayed(() -> getContext().getSystemService(InputMethodManager.class)
+                .displayCompletions(this, cInfo), 40 /* 2~3 frame delay */);
     }
 
     /**
diff --git a/src/com/android/launcher3/logging/InstanceIdSequence.java b/src/com/android/launcher3/logging/InstanceIdSequence.java
index a4b7953..ee6a5a4 100644
--- a/src/com/android/launcher3/logging/InstanceIdSequence.java
+++ b/src/com/android/launcher3/logging/InstanceIdSequence.java
@@ -45,6 +45,13 @@
     }
 
     /**
+     * Constructs a sequence with identifiers [1, InstanceId.INSTANCE_ID_MAX].
+     */
+    public InstanceIdSequence() {
+        this(InstanceId.INSTANCE_ID_MAX);
+    }
+
+    /**
      * Gets the next instance from the sequence.  Safe for concurrent use.
      * @return new InstanceId
      */
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index fee91b0..82d61da 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -19,9 +19,8 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ResourceBasedOverride;
@@ -143,7 +142,37 @@
         LAUNCHER_HOTSEAT_EDU_DENY(481),
 
         @UiEvent(doc = "Hotseat education tip shown")
-        LAUNCHER_HOTSEAT_EDU_ONLY_TIP(482);
+        LAUNCHER_HOTSEAT_EDU_ONLY_TIP(482),
+
+        /**
+         * @deprecated LauncherUiChanged.rank field is repurposed to store all apps rank, so no
+         * separate event is required.
+         */
+        @Deprecated
+        @UiEvent(doc = "App launch ranking logged for all apps predictions")
+        LAUNCHER_ALL_APPS_RANKED(552),
+
+        @UiEvent(doc = "App launch ranking logged for hotseat predictions)")
+        LAUNCHER_HOTSEAT_RANKED(553),
+
+        @UiEvent(doc = "User's workspace layout information is snapshot in the background.")
+        LAUNCHER_WORKSPACE_SNAPSHOT(579),
+
+        @UiEvent(doc = "User tapped on the screenshot button on overview)")
+        LAUNCHER_OVERVIEW_ACTIONS_SCREENSHOT(580),
+
+        @UiEvent(doc = "User tapped on the select button on overview)")
+        LAUNCHER_OVERVIEW_ACTIONS_SELECT(581),
+
+        @UiEvent(doc = "User tapped on the share button on overview")
+        LAUNCHER_OVERVIEW_ACTIONS_SHARE(582),
+
+        @UiEvent(doc = "User tapped on the close button in select mode")
+        LAUNCHER_SELECT_MODE_CLOSE(583),
+
+        @UiEvent(doc = "User tapped on the highlight items in select mode")
+        LAUNCHER_SELECT_MODE_ITEM(584);
+
         // ADD MORE
         private final int mId;
 
@@ -175,6 +204,72 @@
         }
     }
 
+    /**
+     * Helps to construct and write the log message.
+     */
+    public interface StatsLogger {
+
+        /**
+         * Sets log fields from provided {@link ItemInfo}.
+         */
+        default StatsLogger withItemInfo(ItemInfo itemInfo) {
+            return this;
+        }
+
+
+        /**
+         * Sets {@link InstanceId} of log message.
+         */
+        default StatsLogger withInstanceId(InstanceId instanceId) {
+            return this;
+        }
+
+        /**
+         * Sets rank field of log message.
+         */
+        default StatsLogger withRank(int rank) {
+            return this;
+        }
+
+        /**
+         * Sets source launcher state field of log message.
+         */
+        default StatsLogger withSrcState(int srcState) {
+            return this;
+        }
+
+        /**
+         * Sets destination launcher state field of log message.
+         */
+        default StatsLogger withDstState(int dstState) {
+            return this;
+        }
+
+        /**
+         * Sets the final value for container related fields of log message.
+         *
+         * By default container related fields are derived from {@link ItemInfo}, this method would
+         * override those values.
+         */
+        default StatsLogger withContainerInfo(ContainerInfo containerInfo) {
+            return this;
+        }
+
+        /**
+         * Builds the final message and logs it as {@link EventEnum}.
+         */
+        default void log(EventEnum event) {
+        }
+    }
+
+    /**
+     * Returns new logger object.
+     */
+    public StatsLogger logger() {
+        return new StatsLogger() {
+        };
+    }
+
     protected LogStateProvider mStateProvider;
 
     /**
@@ -192,52 +287,6 @@
     }
 
     /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     */
-    public void log(EventEnum event) {
-    }
-
-    /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param instanceId an identifier obtained from an InstanceIdSequence.
-     */
-    public void log(EventEnum event, InstanceId instanceId) {
-    }
-
-    /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param itemInfo item typically containing app or task launch related information.
-     */
-    public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
-    }
-
-    /**
-     * Logs an event and accompanying {@link LauncherState}s.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param launcherAtomItemInfo item typically containing app or task launch related information.
-     */
-    public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo launcherAtomItemInfo,
-            int srcState, int dstState) {
-    }
-
-    /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param instanceId an identifier obtained from an InstanceIdSequence.
-     * @param itemInfo item typically containing app or task launch related information.
-     */
-    public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
-    }
-
-    /**
      * Log an event with ranked-choice information along with package. Does nothing if event.getId()
      * <= 0.
      *
@@ -251,13 +300,6 @@
     }
 
     /**
-     * Logs an event and accompanying {@link LauncherState}s. If either of the state refers
-     * to workspace state, then use pageIndex to pass in index of workspace.
-     */
-    public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
-    }
-
-    /**
      * Logs snapshot, or impression of the current workspace.
      */
     public void logSnapshot() {
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index f1b63f2..835f72d 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -107,8 +107,8 @@
         try {
             intent.send(null, 0, null, null, null, null, activityOptions);
             launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
-            launcher.getStatsLogManager()
-                    .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo);
+            launcher.getStatsLogManager().logger().withItemInfo(mItemInfo)
+                    .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
         } catch (PendingIntent.CanceledException e) {
             e.printStackTrace();
         }
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 8e60c27..61829c0 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -77,8 +77,8 @@
     @Override
     public void onClick(View view) {
         AbstractFloatingView.closeAllOpenViews(mTarget);
-        mTarget.getStatsLogManager()
-                .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo);
+        mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
+                .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP);
 
         final String actionIdentity = mAction.getTitle() + ", "
                 + mItemInfo.getTargetComponent().getPackageName();
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 59d24de..fd292a3 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -119,7 +119,8 @@
             widgetsBottomSheet.populateAndShow(mItemInfo);
             mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                     ControlType.WIDGETS_BUTTON, view);
-            mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP, mItemInfo);
+            mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
+                    .log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
         }
     }
 
@@ -140,8 +141,8 @@
                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
             mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                     ControlType.APPINFO_TARGET, view);
-            mTarget.getStatsLogManager()
-                    .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo);
+            mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
+                    .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
         }
     }
 
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index 9ea8436..b8a184f 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -39,8 +39,18 @@
     }
 
     @Override
+    protected float getDepthUnchecked(Context context) {
+        return 0.15f;
+    }
+
+    @Override
+    public float getOverviewScrimAlpha(Launcher launcher) {
+        return 0.4f;
+    }
+
+    @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(0.9f, 0, 0);
+        return new ScaleAndTranslation(0.92f, 0, 0);
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index de16941..8486666 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -113,7 +113,8 @@
         if (!folder.isOpen() && !folder.isDestroyed()) {
             // Open the requested folder
             folder.animateOpen();
-            StatsLogManager.newInstance(v.getContext()).log(LAUNCHER_FOLDER_OPEN, folder.mInfo);
+            StatsLogManager.newInstance(v.getContext()).logger().withItemInfo(folder.mInfo)
+                    .log(LAUNCHER_FOLDER_OPEN);
         }
     }
 
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 7270ce2..4fa658e 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -174,7 +174,7 @@
 
                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                mLauncher.getStatsLogManager().log(LAUNCHER_WORKSPACE_LONGPRESS);
+                mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
                 OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
             } else {
                 cancelLongPress();
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 90a1c82..d4e074c 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -38,6 +38,7 @@
     public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
     public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
     public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
+    public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
 
     /**
      * Events that either have happened or have not (booleans).
@@ -99,6 +100,13 @@
     }
 
     /**
+     * Marks on-boarding preference boolean at true
+     */
+    public void markChecked(String flag) {
+        mSharedPrefs.edit().putBoolean(flag, true).apply();
+    }
+
+    /**
      * Add 1 to the given event count, if we haven't already reached the max count.
      * @return Whether we have now reached the max count.
      */
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index f54edc2..b010b4b 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -90,13 +90,23 @@
                 }
             };
 
-    // Touch is being dispatched through the normal view dispatch system
-    private static final int TOUCH_DISPATCHING_VIEW = 1 << 0;
+    // Touch coming from normal view system is being dispatched.
+    private static final int TOUCH_DISPATCHING_FROM_VIEW = 1 << 0;
     // Touch is being dispatched through the normal view dispatch system, and started at the
-    // system gesture region
-    private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1;
-    // Touch is being dispatched through a proxy from InputMonitor
-    private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
+    // system gesture region. In this case we prevent internal gesture handling and only allow
+    // normal view event handling.
+    private static final int TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION = 1 << 1;
+    // Touch coming from InputMonitor proxy is being dispatched 'only to gestures'. Note that both
+    // this and view-system can be active at the same time where view-system would go to the views,
+    // and this would go to the gestures.
+    // Note that this is not set when events are coming from proxy, but going through full dispatch
+    // process (both views and gestures) to allow view-system to easily take over in case it
+    // comes later.
+    private static final int TOUCH_DISPATCHING_FROM_PROXY = 1 << 2;
+    // ACTION_DOWN has been dispatched to child views and ACTION_UP or ACTION_CANCEL is pending.
+    // Note that the event source can either be view-dispatching or proxy-dispatching based on if
+    // TOUCH_DISPATCHING_VIEW is present or not.
+    private static final int TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS = 1 << 3;
 
     protected final float[] mTmpXY = new float[2];
     protected final float[] mTmpRectPoints = new float[4];
@@ -204,7 +214,8 @@
 
     protected boolean findActiveController(MotionEvent ev) {
         mActiveController = null;
-        if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) {
+        if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
+                | TOUCH_DISPATCHING_FROM_PROXY)) == 0) {
             // Only look for controllers if we are not dispatching from gesture area and proxy is
             // not active
             mActiveController = findControllerToHandleTouch(ev);
@@ -283,19 +294,28 @@
     public boolean dispatchTouchEvent(MotionEvent ev) {
         switch (ev.getAction()) {
             case ACTION_DOWN: {
-                mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
+                if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
+                    // Cancel the previous touch
+                    int action = ev.getAction();
+                    ev.setAction(ACTION_CANCEL);
+                    super.dispatchTouchEvent(ev);
+                    ev.setAction(action);
+                }
+                mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW
+                        | TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
 
                 if (isEventInLauncher(ev)) {
-                    mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+                    mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
                 } else {
-                    mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
+                    mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
                 }
                 break;
             }
             case ACTION_CANCEL:
             case ACTION_UP:
-                mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
-                mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW;
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
                 break;
         }
         super.dispatchTouchEvent(ev);
@@ -305,43 +325,53 @@
     }
 
     /**
-     * Called before we are about to receive proxy events.
-     *
-     * @return false if we can't handle proxy at this time
-     */
-    public boolean prepareProxyEventStarting() {
-        mProxyTouchController = null;
-        if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) {
-            // We are already dispatching using view system and have an active controller, we can't
-            // handle another controller.
-
-            // This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just
-            // to be safe
-            mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
-            return false;
-        }
-
-        mTouchDispatchState |= TOUCH_DISPATCHING_PROXY;
-        return true;
-    }
-
-    /**
      * Proxies the touch events to the gesture handlers
      */
-    public boolean proxyTouchEvent(MotionEvent ev) {
-        boolean handled;
-        if (mProxyTouchController != null) {
-            handled = mProxyTouchController.onControllerTouchEvent(ev);
+    public boolean proxyTouchEvent(MotionEvent ev, boolean allowViewDispatch) {
+        int actionMasked = ev.getActionMasked();
+        boolean isViewDispatching = (mTouchDispatchState & TOUCH_DISPATCHING_FROM_VIEW) != 0;
+
+        // Only do view dispatch if another view-dispatching is not running, or we already started
+        // proxy-dispatching before. Note that view-dispatching can always take over the proxy
+        // dispatching at anytime, but not vice-versa.
+        allowViewDispatch = allowViewDispatch && !isViewDispatching
+                && (actionMasked == ACTION_DOWN
+                    || ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0));
+
+        if (allowViewDispatch) {
+            mTouchDispatchState |= TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
+            super.dispatchTouchEvent(ev);
+
+            if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) {
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
+            }
+            return true;
         } else {
-            mProxyTouchController = findControllerToHandleTouch(ev);
-            handled = mProxyTouchController != null;
+            boolean handled;
+            if (mProxyTouchController != null) {
+                handled = mProxyTouchController.onControllerTouchEvent(ev);
+            } else {
+                if (actionMasked == ACTION_DOWN) {
+                    if (isViewDispatching && mActiveController != null) {
+                        // A controller is already active, we can't initiate our own controller
+                        mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
+                    } else {
+                        // We will control the handler via proxy
+                        mTouchDispatchState |= TOUCH_DISPATCHING_FROM_PROXY;
+                    }
+                }
+                if ((mTouchDispatchState & TOUCH_DISPATCHING_FROM_PROXY) != 0) {
+                    mProxyTouchController = findControllerToHandleTouch(ev);
+                }
+                handled = mProxyTouchController != null;
+            }
+            if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) {
+                mProxyTouchController = null;
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
+            }
+            return handled;
         }
-        int action = ev.getAction();
-        if (action == ACTION_UP || action == ACTION_CANCEL) {
-            mProxyTouchController = null;
-            mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
-        }
-        return handled;
     }
 
     /**
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 5431ba1..d558781 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -86,7 +86,7 @@
             return false;
         }
         if (item.mEventId.getId() > 0) {
-            mLauncher.getStatsLogManager().log(item.mEventId);
+            mLauncher.getStatsLogManager().logger().log(item.mEventId);
         }
         if (item.mClickListener.onLongClick(view)) {
             close(true);