Merge "Fixing jump when swiping up in landscape and in waterfall cutout" into ub-launcher3-rvc-dev
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 00b0951..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"
@@ -105,6 +110,18 @@
                 <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 0113570..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,9 +16,11 @@
 
 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;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_RANKED;
 
 import android.app.prediction.AppPredictor;
 import android.app.prediction.AppTarget;
@@ -26,6 +28,7 @@
 import android.content.Context;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
@@ -38,8 +41,6 @@
 import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
-import com.android.launcher3.logger.LauncherAtom;
-import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -51,6 +52,7 @@
 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;
 
@@ -306,40 +308,25 @@
     }
 
     /**
-     * Logs ranking info for launched app within all apps prediction.
+     * 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 void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
-        if (itemInfo.getTargetComponent() == null || itemInfo.user == null
-                || (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
-                && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
-            return;
-        }
+    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));
 
-        Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
-        final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-        final List<ComponentKeyMapper> predictedApps = getCurrentState().apps;
-        OptionalInt rank = IntStream.range(0, predictedApps.size())
-                .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
-                .findFirst();
-        if (!rank.isPresent()) {
-            return;
-        }
-
-        LauncherAtom.ItemInfo.Builder atomBuilder = LauncherAtom.ItemInfo.newBuilder();
-        atomBuilder.setRank(rank.getAsInt());
-        atomBuilder.setContainerInfo(
-                LauncherAtom.ContainerInfo.newBuilder().setPredictionContainer(
-                        LauncherAtom.PredictionContainer.newBuilder().build()).build());
-        launcher.getStatsLogManager().log(LAUNCHER_ALL_APPS_RANKED, instanceId,
-                atomBuilder.build());
+        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:
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 522a2dc..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());
@@ -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/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 30a34e4..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,8 @@
 
 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;
@@ -29,9 +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;
 
@@ -54,7 +55,8 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.logger.LauncherAtom;
+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;
@@ -69,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;
 
@@ -120,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) {
@@ -154,36 +166,35 @@
      * 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()));
         }
     }
 
@@ -661,24 +672,25 @@
         if (!rank.isPresent()) {
             return;
         }
-        LauncherAtom.PredictedHotseatContainer.Builder containerBuilder =
-                LauncherAtom.PredictedHotseatContainer.newBuilder();
-        LauncherAtom.ItemInfo.Builder atomBuilder = LauncherAtom.ItemInfo.newBuilder();
+
         int cardinality = 0;
         for (PredictedAppIcon icon : getPredictedIcons()) {
             ItemInfo info = (ItemInfo) icon.getTag();
             cardinality |= 1 << info.screenId;
         }
+
+        PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
         containerBuilder.setCardinality(cardinality);
-        atomBuilder.setRank(rank.getAsInt());
         if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
             containerBuilder.setIndex(rank.getAsInt());
         }
-        atomBuilder.setContainerInfo(
-                LauncherAtom.ContainerInfo.newBuilder().setPredictedHotseatContainer(
-                        containerBuilder).build());
-        mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_RANKED, instanceId,
-                atomBuilder.build());
+        mLauncher.getStatsLogManager().logger()
+                .withInstanceId(instanceId)
+                .withRank(rank.getAsInt())
+                .withContainerInfo(ContainerInfo.newBuilder()
+                        .setPredictedHotseatContainer(containerBuilder)
+                        .build())
+                .log(LAUNCHER_HOTSEAT_RANKED);
     }
 
     private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
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 0e690eb..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;
@@ -45,9 +46,9 @@
 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;
@@ -80,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 {
@@ -108,26 +110,16 @@
     }
 
     @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();
-            });
-        }
-    }
-
-    @Override
     protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
-        super.logAppLaunch(info, 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);
         }
-        PredictionUiStateManager.INSTANCE.get(this).logLaunchedAppRankingInfo(info, instanceId);
     }
 
     @Override
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 4cc8256..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
@@ -26,6 +26,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.graphics.PointF;
 import android.util.Log;
@@ -35,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;
@@ -65,6 +67,9 @@
     // 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();
@@ -110,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,
@@ -132,6 +157,7 @@
         if (mCurrentAnimation == null) {
             return;
         }
+        mNormalToHintOverviewScrimAnimator = null;
         mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
             mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
                 mReachedOverview = true;
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 f62218f..32da52e 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/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 3977598..6380bbe 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
@@ -798,8 +798,10 @@
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
             TaskView taskView = getTaskViewAt(i);
             if (mIgnoreResetTaskId != taskView.getTask().key.id) {
-                taskView.resetVisualProperties();
+                taskView.resetViewTransforms();
                 taskView.setStableAlpha(mContentAlpha);
+                taskView.setFullscreenProgress(mFullscreenProgress);
+                taskView.setModalness(mTaskModalness);
             }
         }
         if (mRunningTaskTileHidden) {
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 411bab4..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
@@ -545,7 +545,7 @@
         setIconAndDimTransitionProgress(iconScale, invert);
     }
 
-    private void resetViewTransforms() {
+    protected void resetViewTransforms() {
         setCurveScale(1);
         setTranslationX(0f);
         setTranslationY(0f);
@@ -554,12 +554,6 @@
         setIconScaleAndDim(1);
     }
 
-    public void resetVisualProperties() {
-        resetViewTransforms();
-        setFullscreenProgress(0);
-        setModalness(0);
-    }
-
     public void setStableAlpha(float parentAlpha) {
         mStableAlpha = parentAlpha;
         setAlpha(mStableAlpha);
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 42ade78..e496807 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.LauncherAppState;
 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;
@@ -48,6 +49,7 @@
 import com.android.systemui.shared.system.SysUiStatsLog;
 
 import java.util.ArrayList;
+import java.util.Optional;
 import java.util.OptionalInt;
 
 /**
@@ -81,38 +83,6 @@
     }
 
     /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param atomInfo item typically containing app or task launch related information.
-     */
-    public void log(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo) {
-        LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
-                new BaseModelUpdateTask() {
-                    @Override
-                    public void execute(LauncherAppState app, BgDataModel dataModel,
-                            AllAppsList apps) {
-                        write(event, instanceId, atomInfo, LAUNCHER_UICHANGED__DST_STATE__HOME,
-                                LAUNCHER_UICHANGED__DST_STATE__BACKGROUND, OptionalInt.empty());
-                    }
-                });
-    }
-
-    /**
-     * 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,
-                srcState, dstState, OptionalInt.empty());
-    }
-
-    /**
      * Logs a ranking event and accompanying {@link InstanceId} and package name.
      */
     @Override
@@ -125,49 +95,6 @@
                 position /* position_picked = 4; */);
     }
 
-    private  void write(EventEnum event, InstanceId instanceId,
-            LauncherAtom.ItemInfo atomInfo,
-            int srcState, int dstState, OptionalInt mRank) {
-        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 */,
-                mRank.orElse(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.
      */
@@ -238,15 +165,22 @@
     /**
      * Helps to construct and write statsd compatible log message.
      */
-    private class StatsCompatLogger implements StatsLogger {
-        private ItemInfo mItemInfo = new ItemInfo();
+    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;
         }
@@ -276,14 +210,35 @@
         }
 
         @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, mItemInfo.buildProto(), mSrcState, mDstState, mRank);
+                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.
@@ -293,12 +248,58 @@
                             public void execute(LauncherAppState app, BgDataModel dataModel,
                                     AllAppsList apps) {
                                 FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
-                                write(event, mInstanceId, mItemInfo.buildProto(folderInfo),
-                                        mSrcState, mDstState, mRank);
+                                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) {
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/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/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/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/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index f72d76f..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;
@@ -145,6 +144,11 @@
         @UiEvent(doc = "Hotseat education tip shown")
         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),
 
@@ -242,6 +246,16 @@
         }
 
         /**
+         * 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) {
@@ -273,25 +287,6 @@
     }
 
     /**
-     * Logs an event.
-     *
-     * @param event an enum implementing EventEnum interface.
-     * @param atomInfo item typically containing app or task launch related information.
-     */
-    public void log(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo) {
-    }
-
-    /**
-     * 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) {
-    }
-
-    /**
      * Log an event with ranked-choice information along with package. Does nothing if event.getId()
      * <= 0.
      *
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/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.
      */