Merge "Remove RPC due to unnecessary permission check" into main
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 14605d8..7e824ec 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -67,6 +67,7 @@
 
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:name="com.android.launcher3.LauncherApplication"
         android:fullBackupOnly="true"
         android:backupInForeground="true"
         android:fullBackupContent="@xml/backupscheme"
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 3dc30dc..5f58de5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -18,8 +18,9 @@
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
+import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
-import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.content.Context;
@@ -172,12 +173,14 @@
 
     @DrawableRes
     private int getAllAppsButton(boolean isTransientTaskbar) {
+        boolean shouldSelectTransientIcon = (isTransientTaskbar || ENABLE_TASKBAR_PINNING.get())
+                && !mActivityContext.isThreeButtonNav();
         if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
-            return isTransientTaskbar
+            return shouldSelectTransientIcon
                     ? R.drawable.ic_transient_taskbar_all_apps_search_button
                     : R.drawable.ic_taskbar_all_apps_search_button;
         } else {
-            return isTransientTaskbar
+            return shouldSelectTransientIcon
                     ? R.drawable.ic_transient_taskbar_all_apps_button
                     : R.drawable.ic_taskbar_all_apps_button;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
index 5253e7a..a922ed1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
@@ -24,6 +24,10 @@
 import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT;
 import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE;
 import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS;
 import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
@@ -118,9 +122,7 @@
         if (FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
             addAllAppsFromOverviewCatergory();
         }
-        if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
-            addCustomLpnhCatergory();
-        }
+        addCustomLpnhCategory();
 
         if (getActivity() != null) {
             getActivity().setTitle("Developer Options");
@@ -423,13 +425,25 @@
                 105, 500, 100, ALL_APPS_OVERVIEW_THRESHOLD));
     }
 
-    private void addCustomLpnhCatergory() {
+    private void addCustomLpnhCategory() {
         PreferenceCategory category = newCategory("Long Press Nav Handle Config");
-        category.addPreference(createSeekBarPreference("Slop multiplier (applied to edge slop, "
-                        + "which is generally already 50% higher than touch slop)",
-                25, 200, 100, LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE));
-        category.addPreference(createSeekBarPreference("Trigger milliseconds",
-                100, 500, 1, LONG_PRESS_NAV_HANDLE_TIMEOUT_MS));
+        if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
+            category.addPreference(createSeekBarPreference("Slop multiplier (applied to edge slop, "
+                            + "which is generally already 50% higher than touch slop)",
+                    25, 200, 100, LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE));
+            category.addPreference(createSeekBarPreference("Trigger milliseconds",
+                    100, 500, 1, LONG_PRESS_NAV_HANDLE_TIMEOUT_MS));
+        }
+        if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) {
+            category.addPreference(createSeekBarPreference("Haptic hint start scale",
+                    0, 100, 100, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT));
+            category.addPreference(createSeekBarPreference("Haptic hint end scale",
+                    0, 100, 100, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT));
+            category.addPreference(createSeekBarPreference("Haptic hint scale exponent",
+                    1, 5, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT));
+            category.addPreference(createSeekBarPreference("Haptic hint iterations (12 ms each)",
+                    0, 100, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS));
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
index 7a2b343..14305cf 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -46,4 +46,19 @@
     public @Nullable Runnable getLongPressRunnable() {
         return null;
     }
+
+    /**
+     * Called when nav handle gesture starts. Returns true if long press nav handle is enabled and
+     * supported.
+     */
+    public boolean canStartTouch() {
+        return false;
+    }
+
+    /**
+     * Called when nav handle gesture is finished by the user lifting their finger or the system
+     * cancelling the touch for some other reason.
+     */
+    public void onTouchFinished() {
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index 1af4bad..f11e537 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -80,7 +80,8 @@
                     mCurrentDownEvent.recycle();
                 }
                 mCurrentDownEvent = MotionEvent.obtain(ev);
-                if (isInNavBarHorizontalArea(ev.getRawX())) {
+                if (isInNavBarHorizontalArea(ev.getRawX())
+                        && mNavHandleLongPressHandler.canStartTouch()) {
                     MAIN_EXECUTOR.getHandler().postDelayed(mTriggerLongPress,
                             mLongPressTimeout);
                 }
@@ -133,6 +134,7 @@
 
     private void cancelLongPress() {
         MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress);
+        mNavHandleLongPressHandler.onTouchFinished();
     }
 
     private boolean isInNavBarHorizontalArea(float x) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index f6a9440..12a8bd9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -20,9 +20,9 @@
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 
+import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
 import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING;
 
 import android.content.Context;
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index e0b5272..61ba5ac 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.HorizontalInsettableView;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.unfold.LauncherJankMonitorTransitionProgressListener;
 import com.android.quickstep.util.unfold.PreemptiveUnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
@@ -91,6 +92,8 @@
         }
 
         unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider);
+        unfoldTransitionProgressProvider.addCallback(
+                new LauncherJankMonitorTransitionProgressListener(launcher::getRootView));
 
         mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
                 windowManager, rotationChangeProvider);
diff --git a/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt b/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt
new file mode 100644
index 0000000..4f89c7e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.quickstep.util.unfold
+
+import android.view.View
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.util.function.Supplier
+
+/** Reports beginning and end of the unfold animation to interaction jank monitor */
+class LauncherJankMonitorTransitionProgressListener(
+    private val attachedViewProvider: Supplier<View>
+) : TransitionProgressListener {
+
+    override fun onTransitionStarted() {
+        InteractionJankMonitorWrapper.begin(
+            attachedViewProvider.get(),
+            InteractionJankMonitorWrapper.CUJ_LAUNCHER_UNFOLD_ANIM
+        )
+    }
+
+    override fun onTransitionFinished() {
+        InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_LAUNCHER_UNFOLD_ANIM)
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 71758ad..a4a53d1 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -2,7 +2,7 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell;
 
@@ -169,7 +169,9 @@
                 mIconLoadRequest2 = iconCache.updateIconInBackground(mSecondaryTask,
                         (task) -> {
                             setIcon(mIconView2, task.icon);
-                            setText(mIconView2, TaskUtils.getTitle(getContext(), task));
+                            if (enableOverviewIconMenu()) {
+                                setText(mIconView2, TaskUtils.getTitle(getContext(), task));
+                            }
                             mDigitalWellBeingToast2.initialize(mSecondaryTask);
                             mDigitalWellBeingToast2.setSplitConfiguration(mSplitBoundsConfig);
                             mDigitalWellBeingToast.setSplitConfiguration(mSplitBoundsConfig);
@@ -184,7 +186,9 @@
             }
             if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
                 setIcon(mIconView2, null);
-                setText(mIconView2, null);
+                if (enableOverviewIconMenu()) {
+                    setText(mIconView2, null);
+                }
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.java b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
index 960a09e..5a2e135 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.java
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
@@ -16,7 +16,7 @@
 package com.android.quickstep.views;
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
index 222f9ca..64caba7 100644
--- a/quickstep/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/src/com/android/quickstep/views/IconView.java
@@ -15,7 +15,7 @@
  */
 package com.android.quickstep.views;
 
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 
 import android.content.Context;
 import android.graphics.Canvas;
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 62c0bef..8f12909 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -17,7 +17,7 @@
 package com.android.quickstep.views;
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
 
 import android.animation.Animator;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index dbdf058..beb10ef 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -23,9 +23,10 @@
 import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
 import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -115,8 +116,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 
-import kotlin.Unit;
-
 import java.lang.annotation.Retention;
 import java.util.Arrays;
 import java.util.Collections;
@@ -125,6 +124,8 @@
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
+import kotlin.Unit;
+
 /**
  * A task in the Recents view.
  */
@@ -445,7 +446,7 @@
 
         boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
                 || DesktopTaskView.DESKTOP_MODE_SUPPORTED;
-        boolean cursorHoverStatesEnabled = FeatureFlags.enableCursorHoverStates();
+        boolean cursorHoverStatesEnabled = enableCursorHoverStates();
 
         setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled);
 
@@ -561,7 +562,7 @@
 
     @Override
     public boolean onInterceptHoverEvent(MotionEvent event) {
-        if (FeatureFlags.enableCursorHoverStates()) {
+        if (enableCursorHoverStates()) {
             // avoid triggering hover event on child elements which would cause HOVER_EXIT for this
             // task view
             return true;
@@ -1092,7 +1093,9 @@
                 mIconLoadRequest = iconCache.updateIconInBackground(mTask,
                         (task) -> {
                             setIcon(mIconView, task.icon);
-                            setText(mIconView, TaskUtils.getTitle(getContext(), task));
+                            if (enableOverviewIconMenu()) {
+                                setText(mIconView, TaskUtils.getTitle(getContext(), task));
+                            }
                             mDigitalWellBeingToast.initialize(task);
                         });
             }
@@ -1108,7 +1111,9 @@
             }
             if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
                 setIcon(mIconView, null);
-                setText(mIconView, null);
+                if (enableOverviewIconMenu()) {
+                    setText(mIconView, null);
+                }
             }
         }
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 61f1f74..d45c225 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -16,7 +16,6 @@
 
 package com.android.quickstep;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
 
@@ -45,7 +44,6 @@
 import com.android.launcher3.tapl.OverviewTask;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -226,7 +224,7 @@
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
     public void testSwitchToOverviewWithStashedTaskbar() throws Exception {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
+        try {
             startTestAppsWithCheck();
             // Set ignoreTaskbarVisibility, as transient taskbar will be stashed after app launch.
             mLauncher.setIgnoreTaskbarVisibility(true);
@@ -356,6 +354,7 @@
     @PortraitLandscape
     @TaskbarModeSwitch(mode = PERSISTENT)
     @PlatinumTest(focusArea = "launcher")
+    @ScreenRecord
     public void testOverviewForTablet() throws Exception {
         assumeTrue(mLauncher.isTablet());
 
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
index fc23a05..db23cc0 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
@@ -15,14 +15,15 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
+import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
 
+import static org.junit.Assume.assumeTrue;
+
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.util.TestUtil;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
 import org.junit.Test;
@@ -35,42 +36,32 @@
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
     public void testShowTaskbarUnstashHintOnHover() {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
+        assumeTrue(enableCursorHoverStates());
+        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+        mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint();
     }
 
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
     public void testUnstashTaskbarOnScreenBottomEdgeHover() {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
+        assumeTrue(enableCursorHoverStates());
+        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+        mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar();
     }
 
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
     public void testHoverBelowHintedTaskbarToUnstash() {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
+        assumeTrue(enableCursorHoverStates());
+        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+        mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash();
     }
 
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
     public void testClickHoveredTaskbarToGoHome() throws Exception {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome();
-        }
+        assumeTrue(enableCursorHoverStates());
+        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+        mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome();
     }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d7b50a0..ddcb1e6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -17,8 +17,9 @@
 package com.android.launcher3;
 
 import static android.text.Layout.Alignment.ALIGN_NORMAL;
+
+import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
-import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c96e22d..2ac6098 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,7 +24,7 @@
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.pxFromSp;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH;
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4a9add4..b629ec2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2600,21 +2600,20 @@
                 .logEnd(isBindSync
                         ? LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
                         : LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC);
-        // In the first rootview's onDraw after onInitialBindComplete(), log end of startup latency.
+        MAIN_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
+            mStartupLatencyLogger
+                    .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
+                    .log()
+                    .reset();
+            if (mIsColdStartupAfterReboot) {
+                Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME,
+                        COLD_STARTUP_TRACE_COOKIE);
+            }
+        });
         getRootView().getViewTreeObserver().addOnDrawListener(
                 new ViewTreeObserver.OnDrawListener() {
-
                     @Override
                     public void onDraw() {
-                        mStartupLatencyLogger
-                                .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
-                                .log()
-                                .reset();
-                        if (mIsColdStartupAfterReboot) {
-                            Trace.endAsyncSection(COLD_STARTUP_TRACE_METHOD_NAME,
-                                    COLD_STARTUP_TRACE_COOKIE);
-                        }
-
                         MAIN_EXECUTOR.getHandler().postAtFrontOfQueue(
                                 () -> getRootView().getViewTreeObserver()
                                         .removeOnDrawListener(this));
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 8d19040..34bfdb7 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -64,12 +64,6 @@
 public class LauncherAppState implements SafeCloseable {
 
     public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
-    public static final String KEY_ICON_STATE = "pref_icon_shape_path";
-    public static final String KEY_ALL_APPS_OVERVIEW_THRESHOLD = "pref_all_apps_overview_threshold";
-    public static final String KEY_LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
-            "pref_long_press_nav_handle_slop_multiplier";
-    public static final String KEY_LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
-            "pref_long_press_nav_handle_timeout_ms";
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
new file mode 100644
index 0000000..40873be
--- /dev/null
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import android.app.Application;
+
+/**
+ * Main application class for Launcher
+ */
+public class LauncherApplication extends Application {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        MainProcessInitializer.initialize(this);
+    }
+}
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 9a0d02a..c6a9283 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -297,31 +297,59 @@
         @JvmField
         val ICON_STATE =
             nonRestorableItem(
-                LauncherAppState.KEY_ICON_STATE,
+                    "pref_icon_shape_path",
                 "",
                 EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
         @JvmField
         val ALL_APPS_OVERVIEW_THRESHOLD =
             nonRestorableItem(
-                LauncherAppState.KEY_ALL_APPS_OVERVIEW_THRESHOLD,
+                    "pref_all_apps_overview_threshold",
                 180,
                 EncryptionType.MOVE_TO_DEVICE_PROTECTED
             )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
-            nonRestorableItem(
-                LauncherAppState.KEY_LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE,
-                100,
-                EncryptionType.MOVE_TO_DEVICE_PROTECTED
-            )
+                nonRestorableItem(
+                        "pref_long_press_nav_handle_slop_multiplier",
+                        100,
+                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
-            nonRestorableItem(
-                LauncherAppState.KEY_LONG_PRESS_NAV_HANDLE_TIMEOUT_MS,
-                ViewConfiguration.getLongPressTimeout(),
-                EncryptionType.MOVE_TO_DEVICE_PROTECTED
-            )
+                nonRestorableItem(
+                        "pref_long_press_nav_handle_timeout_ms",
+                        ViewConfiguration.getLongPressTimeout(),
+                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                )
+        @JvmField
+        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
+                nonRestorableItem(
+                        "pref_long_press_nav_handle_haptic_hint_start_scale_percent",
+                        0,
+                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                )
+        @JvmField
+        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
+                nonRestorableItem(
+                        "pref_long_press_nav_handle_haptic_hint_end_scale_percent",
+                        50,
+                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                )
+        @JvmField
+        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
+                nonRestorableItem(
+                        "pref_long_press_nav_handle_haptic_hint_scale_exponent",
+                        2,
+                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                )
+        @JvmField
+        val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
+                nonRestorableItem(
+                        "pref_long_press_nav_handle_haptic_hint_iterations",
+                        40,
+                        EncryptionType.MOVE_TO_DEVICE_PROTECTED
+                )
         @JvmField
         val THEMED_ICONS =
             backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 440e146..4e0ba62 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -33,7 +33,6 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.ModelDbController;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 
@@ -44,8 +43,6 @@
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
 
-    public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
-
     /**
      * $ adb shell dumpsys activity provider com.android.launcher3
      */
@@ -60,13 +57,6 @@
 
     @Override
     public boolean onCreate() {
-        if (FeatureFlags.IS_STUDIO_BUILD) {
-            Log.d(TAG, "Launcher process started");
-        }
-
-        // The content provider exists for the entire duration of the launcher main process and
-        // is the first component to get created.
-        MainProcessInitializer.initialize(getContext().getApplicationContext());
         return true;
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c09a5b9..278e4e5 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -373,7 +373,9 @@
                         new VibrationAnimatorUpdateListener(this, mVibratorWrapper,
                                 0, SWIPE_DRAG_COMMIT_THRESHOLD));
             }
-            builder.addEndListener(mVibratorWrapper::cancelVibrate);
+            builder.addEndListener((unused) -> {
+                mVibratorWrapper.cancelVibrate();
+            });
         }
 
         float targetProgress = toState.getVerticalProgress(mLauncher);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8610efe..2ca8a1e 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -262,6 +262,10 @@
             getReleaseFlag(282993230, "ENABLE_LONG_PRESS_NAV_HANDLE", TEAMFOOD,
                     "Enables long pressing on the bottom bar nav handle to trigger events.");
 
+    public static final BooleanFlag ENABLE_SEARCH_HAPTIC_HINT =
+            getReleaseFlag(303023676, "ENABLE_SEARCH_HAPTIC_HINT", TEAMFOOD,
+                    "Enables haptic hint when long pressing on the bottom bar nav handle.");
+
     // TODO(Block 17): Clean up flags
     public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(270396583,
             "ENABLE_TASKBAR_PINNING", DISABLED,
@@ -319,24 +323,6 @@
                     + "waiting for SystemUI and then merging the SystemUI progress whenever we "
                     + "start receiving the events");
 
-    // Aconfig migration complete for ENABLE_OVERVIEW_ICON_MENU.
-    @VisibleForTesting
-    public static final BooleanFlag ENABLE_OVERVIEW_ICON_MENU = getDebugFlag(257950105,
-            "ENABLE_OVERVIEW_ICON_MENU", TEAMFOOD,
-            "Enable updated overview icon and menu within task.");
-    public static boolean enableOverviewIconMenu() {
-        return ENABLE_OVERVIEW_ICON_MENU.get() || Flags.enableOverviewIconMenu();
-    }
-
-    // Aconfig migration complete for ENABLE_CURSOR_HOVER_STATES.
-    @VisibleForTesting
-    public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650,
-            "ENABLE_CURSOR_HOVER_STATES", TEAMFOOD,
-            "Enables cursor hover states for certain elements.");
-    public static boolean enableCursorHoverStates() {
-        return ENABLE_CURSOR_HOVER_STATES.get() || Flags.enableCursorHoverStates();
-    }
-
     // TODO(Block 24): Clean up flags
     public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(270393455,
             "ENABLE_NEW_MIGRATION_LOGIC", ENABLED,
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index d00d901..cb1dc4f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.folder;
 
-import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
+import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index ae5d8d4..18200f6 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2023 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.graphics;
 
 import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
@@ -21,6 +36,7 @@
 import android.os.Messenger;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.GridOption;
@@ -70,7 +86,11 @@
 
     private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337;
 
-    private final ArrayMap<IBinder, PreviewLifecycleObserver> mActivePreviews = new ArrayMap<>();
+    /**
+     * Here we use the IBinder and the screen ID as the key of the active previews.
+     */
+    private final ArrayMap<Pair<IBinder, Integer>, PreviewLifecycleObserver> mActivePreviews =
+            new ArrayMap<>();
 
     @Override
     public boolean onCreate() {
@@ -176,11 +196,10 @@
         try {
             PreviewSurfaceRenderer renderer = new PreviewSurfaceRenderer(getContext(), request);
 
-            // Destroy previous
-            destroyObserver(mActivePreviews.get(renderer.getHostToken()));
-
             observer = new PreviewLifecycleObserver(renderer);
-            mActivePreviews.put(renderer.getHostToken(), observer);
+            // Destroy previous
+            destroyObserver(mActivePreviews.get(observer.getIdentifier()));
+            mActivePreviews.put(observer.getIdentifier(), observer);
 
             renderer.loadAsync();
             renderer.getHostToken().linkToDeath(observer, 0);
@@ -210,9 +229,9 @@
         observer.destroyed = true;
         observer.renderer.getHostToken().unlinkToDeath(observer, 0);
         Executors.MAIN_EXECUTOR.execute(observer.renderer::destroy);
-        PreviewLifecycleObserver cached = mActivePreviews.get(observer.renderer.getHostToken());
+        PreviewLifecycleObserver cached = mActivePreviews.get(observer.getIdentifier());
         if (cached == observer) {
-            mActivePreviews.remove(observer.renderer.getHostToken());
+            mActivePreviews.remove(observer.getIdentifier());
         }
     }
 
@@ -242,5 +261,14 @@
         public void binderDied() {
             destroyObserver(this);
         }
+
+        /**
+         * Returns a key that should make the PreviewSurfaceRenderer unique and if two of them have
+         * the same key they will be treated as the same PreviewSurfaceRenderer. Primary this is
+         * used to prevent memory leaks by removing the old PreviewSurfaceRenderer.
+         */
+        public Pair<IBinder, Integer> getIdentifier() {
+            return new Pair<>(renderer.getHostToken(), renderer.getDisplayId());
+        }
     }
 }
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index aebcdd4..683354b 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -114,12 +114,17 @@
         mDisplay = context.getSystemService(DisplayManager.class)
                 .getDisplay(bundle.getInt(KEY_DISPLAY_ID));
 
-        mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() -> new SurfaceControlViewHost(mContext,
-                context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY),
-                mHostToken)).get(5, TimeUnit.SECONDS);
+        mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() ->
+                new SurfaceControlViewHost(mContext, context.getSystemService(DisplayManager.class)
+                        .getDisplay(DEFAULT_DISPLAY), mHostToken)
+        ).get(5, TimeUnit.SECONDS);
         mOnDestroyCallbacks.add(mSurfaceControlViewHost::release);
     }
 
+    public int getDisplayId() {
+        return mDisplay.getDisplayId();
+    }
+
     public IBinder getHostToken() {
         return mHostToken;
     }
@@ -225,7 +230,7 @@
             PreviewContext previewContext = new PreviewContext(inflationContext, idp);
             // Copy existing data to preview DB
             LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
-                    .getModel().getModelDbController().getDb(),
+                            .getModel().getModelDbController().getDb(),
                     TABLE_NAME,
                     LauncherAppState.getInstance(previewContext)
                             .getModel().getModelDbController().getDb(),
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index d434ad2..c80f243 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -27,7 +27,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index b3189b7..62ef229 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -27,7 +27,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index dac7964..4409572 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -22,7 +22,7 @@
 import static android.view.Gravity.RIGHT;
 import static android.view.Gravity.START;
 
-import static com.android.launcher3.config.FeatureFlags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableOverviewIconMenu;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index 91945ca..80a9bae 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -18,6 +18,10 @@
 import static android.os.VibrationEffect.createPredefined;
 import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
 
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
@@ -35,10 +39,9 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.PendingAnimation;
-
-import java.util.function.Consumer;
+import com.android.launcher3.config.FeatureFlags;
 
 /**
  * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
@@ -70,7 +73,7 @@
     private final VibrationEffect mBumpEffect;
 
     @Nullable
-    private final VibrationEffect mAssistEffect;
+    private final VibrationEffect mSearchEffect;
 
     private long mLastDragTime;
     private final int mThresholdUntilNextDragCallMillis;
@@ -80,12 +83,14 @@
      */
     public static final VibrationEffect OVERVIEW_HAPTIC = EFFECT_CLICK;
 
+    private final Context mContext;
     private final Vibrator mVibrator;
     private final boolean mHasVibrator;
 
     private boolean mIsHapticFeedbackEnabled;
 
     private VibratorWrapper(Context context) {
+        mContext = context;
         mVibrator = context.getSystemService(Vibrator.class);
         mHasVibrator = mVibrator.hasVibrator();
         if (mHasVibrator) {
@@ -133,14 +138,20 @@
         if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported(
                 VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
                 VibrationEffect.Composition.PRIMITIVE_TICK)) {
-            // quiet ramp, short pause, then sharp tick
-            mAssistEffect = VibrationEffect.startComposition()
-                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
-                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
-                    .compose();
+            if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) {
+                mSearchEffect = VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f)
+                        .compose();
+            } else {
+                // quiet ramp, short pause, then sharp tick
+                mSearchEffect = VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+                        .compose();
+            }
         } else {
             // fallback for devices without composition support
-            mAssistEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
+            mSearchEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
         }
     }
 
@@ -184,20 +195,10 @@
     }
 
     /**
-     * The assist haptic is used to be called when an assistant is invoked
-     */
-    public void vibrateForAssist() {
-        if (mAssistEffect != null) {
-            vibrate(mAssistEffect);
-        }
-    }
-
-    /**
      * This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For
-     * example, when no animation is happening but a vibrator happens to be vibrating still. Need
-     * boolean parameter for {@link PendingAnimation#addEndListener(Consumer)}.
+     * example, when no animation is happening but a vibrator happens to be vibrating still.
      */
-    public void cancelVibrate(boolean unused) {
+    public void cancelVibrate() {
         UI_HELPER_EXECUTOR.execute(mVibrator::cancel);
         // reset dragTexture timestamp to be able to play dragTexture again whenever cancelled
         mLastDragTime = 0;
@@ -233,4 +234,37 @@
             });
         }
     }
+
+    /** Indicates that search has been invoked. */
+    public void vibrateForSearch() {
+        if (mSearchEffect != null) {
+            vibrate(mSearchEffect);
+        }
+    }
+
+    /** Indicates that search will be invoked if the current gesture is maintained. */
+    public void vibrateForSearchHint() {
+        if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get() && Utilities.ATLEAST_S
+                && mVibrator.areAllPrimitivesSupported(
+                VibrationEffect.Composition.PRIMITIVE_LOW_TICK)) {
+            float startScale = LauncherPrefs.get(mContext).get(
+                    LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT) / 100f;
+            float endScale = LauncherPrefs.get(mContext).get(
+                    LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT) / 100f;
+            int scaleExponent = LauncherPrefs.get(mContext).get(
+                    LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT);
+            int iterations = LauncherPrefs.get(mContext).get(
+                    LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS);
+
+            VibrationEffect.Composition composition = VibrationEffect.startComposition();
+            for (int i = 0; i < iterations; i++) {
+                float t = i / (iterations - 1f);
+                float scale = (float) Math.pow((1 - t) * startScale + t * endScale,
+                        scaleExponent);
+                composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scale);
+            }
+
+            vibrate(composition.compose());
+        }
+    }
 }
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index 91480e1..ec3b642 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -67,7 +67,8 @@
             return false;
         }
         WindowBounds other = (WindowBounds) obj;
-        return other.bounds.equals(bounds) && other.insets.equals(insets);
+        return other.bounds.equals(bounds) && other.insets.equals(insets)
+                && other.rotationHint == rotationHint;
     }
 
     @Override
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 56a74a9..669aaab 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -89,7 +89,7 @@
 import java.util.function.Supplier;
 
 /**
- * Base class for all instrumentation tests providing various utility methods.
+ * Base class for all instrumentation tests providing various utility methods. RUN 4
  */
 public abstract class AbstractLauncherUiTest {
 
@@ -252,6 +252,12 @@
     public void setUp() throws Exception {
         mLauncher.onTestStart();
 
+        if (TestStabilityRule.isPresubmit()) {
+            aggressivelyUnlockSysUi();
+        } else {
+            verifyKeyguardInvisible();
+        }
+
         final String launcherPackageName = mDevice.getLauncherPackageName();
         try {
             final Context context = InstrumentationRegistry.getContext();
@@ -285,7 +291,27 @@
         verifyKeyguardInvisible();
     }
 
-    /** Fail if lock screen is present */
+    private boolean hasSystemUiObject(String resId) {
+        return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
+    }
+
+    // Seeing if this will decrease: b/303755862
+    void aggressivelyUnlockSysUi() {
+        for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
+            Log.d(TAG, "Before attempting to unlock the phone");
+            try {
+                mDevice.executeShellCommand("input keyevent 82");
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            mDevice.waitForIdle();
+        }
+        Assert.assertTrue("Keyguard still visible",
+                TestHelpers.wait(
+                        Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
+        Log.d(TAG, "Keyguard is not visible");
+    }
+
     public static void verifyKeyguardInvisible() {
         final boolean keyguardAlreadyVisible = sSeenKeyguard;
 
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index b8ca43f..d94e4a5 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -1,7 +1,5 @@
 package com.android.launcher3.ui;
 
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
-
 import android.util.Log;
 import android.view.Surface;
 
@@ -38,7 +36,7 @@
                 // If running in presubmit, don't run in both orientations.
                 // It's important to keep presubmits fast even if we will occasionally miss
                 // regressions in presubmit.
-                || TestStabilityRule.getRunFlavor() == PLATFORM_PRESUBMIT) {
+                || TestStabilityRule.isPresubmit()) {
             return base;
         }
 
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 38de071..b51045f 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -146,4 +146,8 @@
 
         return sRunFlavor;
     }
+
+    public static boolean isPresubmit() {
+        return getRunFlavor() == PLATFORM_PRESUBMIT;
+    }
 }