Snap for 9576191 from 96328e98d1c43a2768a717afb0d6c0cf77d8b7f9 to tm-qpr3-release

Change-Id: I988af5945d811adc4f76a10fc8333773b0159cf6
diff --git a/Android.bp b/Android.bp
index 0bbb3d2..e730c9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -80,10 +80,10 @@
         "androidx.preference_preference",
         "SystemUISharedLib",
         "SystemUIAnimationLib",
+        "launcher-testing-shared",
     ],
     srcs: [
         "tests/tapl/**/*.java",
-        "src/com/android/launcher3/testing/shared/**/*.java",
     ],
     resource_dirs: [ ],
     manifest: "tests/tapl/AndroidManifest.xml",
@@ -169,7 +169,10 @@
 android_library {
     name: "Launcher3CommonDepsLib",
     srcs: ["src_build_config/**/*.java"],
-    static_libs: ["Launcher3ResLib"],
+    static_libs: [
+        "Launcher3ResLib",
+        "launcher-testing-shared",
+    ],
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
     manifest: "AndroidManifest-common.xml",
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 0d8391b..3846a9c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -297,15 +297,11 @@
     <!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
     <dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
     <!-- Taskbar swipe up thresholds -->
-    <dimen name="taskbar_nav_threshold">40dp</dimen>
-    <dimen name="taskbar_app_window_threshold">150dp</dimen>
-    <dimen name="taskbar_home_overview_threshold">225dp</dimen>
+    <dimen name="taskbar_nav_threshold">30dp</dimen>
+    <dimen name="taskbar_app_window_threshold">100dp</dimen>
+    <dimen name="taskbar_home_overview_threshold">180dp</dimen>
     <dimen name="taskbar_catch_up_threshold">300dp</dimen>
 
-    <dimen name="taskbar_nav_threshold_v2">30dp</dimen>
-    <dimen name="taskbar_app_window_threshold_v2">100dp</dimen>
-    <dimen name="taskbar_home_overview_threshold_v2">180dp</dimen>
-
     <!--  Taskbar 3 button spacing  -->
     <dimen name="taskbar_button_space_inbetween">24dp</dimen>
     <dimen name="taskbar_button_space_inbetween_phone">40dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 9cec881..728c91f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -20,6 +20,7 @@
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
+import static com.android.launcher3.LauncherAnimUtils.ROTATION_DRAWABLE_PERCENT;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
@@ -60,6 +61,7 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.RotateDrawable;
 import android.inputmethodservice.InputMethodService;
 import android.os.Handler;
 import android.util.Property;
@@ -172,10 +174,10 @@
     // Initialized in init.
     private TaskbarControllers mControllers;
     private boolean mIsImeRenderingNavButtons;
-    private View mA11yButton;
+    private ImageView mA11yButton;
     private int mSysuiStateFlags;
-    private View mBackButton;
-    private View mHomeButton;
+    private ImageView mBackButton;
+    private ImageView mHomeButton;
     private MultiValueAlpha mBackButtonAlpha;
     private MultiValueAlpha mHomeButtonAlpha;
     private FloatingRotationButton mFloatingRotationButton;
@@ -186,7 +188,7 @@
     private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer =
             this::onComputeInsetsForSeparateWindow;
     private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
-    private View mRecentsButton;
+    private ImageView mRecentsButton;
 
     public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
         mContext = context;
@@ -353,13 +355,13 @@
                     return (flags & FLAG_DISABLE_BACK) == 0
                             && ((flags & FLAG_KEYGUARD_VISIBLE) == 0 || showingOnKeyguard);
                 }));
-        boolean isRtl = Utilities.isRtl(mContext.getResources());
         mPropertyHolders.add(new StatePropertyHolder(mBackButton,
-                flags -> (flags & FLAG_IME_VISIBLE) != 0 && !mContext.isNavBarKidsModeActive(),
-                View.ROTATION, isRtl ? 90 : -90, 0));
+                flags -> (flags & FLAG_IME_VISIBLE) != 0,
+                ROTATION_DRAWABLE_PERCENT, 1f, 0f));
         // Translate back button to be at end/start of other buttons for keyguard
         int navButtonSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.taskbar_nav_buttons_size);
+        boolean isRtl = Utilities.isRtl(mContext.getResources());
         mPropertyHolders.add(new StatePropertyHolder(
                 mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0
                         || (flags & FLAG_KEYGUARD_VISIBLE) != 0,
@@ -734,13 +736,18 @@
             int paddingBottom = paddingTop;
 
             // Update icons
-            ((ImageView) mBackButton).setImageDrawable(
-                    mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
-            ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+            final RotateDrawable rotateDrawable = new RotateDrawable();
+            rotateDrawable.setDrawable(mContext.getDrawable(R.drawable.ic_sysbar_back_kids));
+            rotateDrawable.setFromDegrees(0f);
+            rotateDrawable.setToDegrees(-90f);
+            mBackButton.setImageDrawable(rotateDrawable);
+            mBackButton.setScaleType(ImageView.ScaleType.FIT_CENTER);
             mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
-            ((ImageView) mHomeButton).setImageDrawable(
+            mBackButton.setScaleX(Utilities.isRtl(mContext.getResources()) ? -1f : 1f);
+
+            mHomeButton.setImageDrawable(
                     mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
-            ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+            mHomeButton.setScaleType(ImageView.ScaleType.FIT_CENTER);
             mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
 
             // Home button layout
@@ -781,6 +788,12 @@
 
             mHomeButton.setOnLongClickListener(null);
         } else if (mContext.isThreeButtonNav()) {
+            final RotateDrawable rotateDrawable = new RotateDrawable();
+            rotateDrawable.setDrawable(mContext.getDrawable(R.drawable.ic_sysbar_back));
+            rotateDrawable.setFromDegrees(0f);
+            rotateDrawable.setToDegrees(Utilities.isRtl(mContext.getResources()) ? 90f : -90f);
+            mBackButton.setImageDrawable(rotateDrawable);
+
             // Setup normal 3 button
             // Add spacing after the end of the last nav button
             FrameLayout.LayoutParams navButtonParams =
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 2485ff8..4f10dde 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -27,7 +27,6 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_REVISED_THRESHOLDS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -367,17 +366,12 @@
         TaskbarUIController controller = mActivityInterface.getTaskbarController();
         mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
         mIsTaskbarAllAppsOpen = controller != null && controller.isTaskbarAllAppsOpen();
-        mTaskbarAppWindowThreshold = res
-                .getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
-                        ? R.dimen.taskbar_app_window_threshold_v2
-                        : R.dimen.taskbar_app_window_threshold);
+        mTaskbarAppWindowThreshold =
+                res.getDimensionPixelSize(R.dimen.taskbar_app_window_threshold);
         boolean swipeWillNotShowTaskbar = mTaskbarAlreadyOpen;
         mTaskbarHomeOverviewThreshold = swipeWillNotShowTaskbar
                 ? 0
-                : res.getDimensionPixelSize(
-                        ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
-                                ? R.dimen.taskbar_home_overview_threshold_v2
-                                : R.dimen.taskbar_home_overview_threshold);
+                : res.getDimensionPixelSize(R.dimen.taskbar_home_overview_threshold);
         mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
     }
 
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 89e8f51..7f2886c 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -346,7 +346,6 @@
             case RECENTS:
                 return OVERVIEW;
             case NEW_TASK:
-                return QUICK_SWITCH_FROM_HOME;
             case LAST_TASK:
                 return BACKGROUND_APP;
             case HOME:
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 3afd0a3..1630d0f 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -18,7 +18,6 @@
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 
 import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_REVISED_THRESHOLDS;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING;
 
 import android.content.Context;
@@ -74,9 +73,7 @@
 
         Resources res = context.getResources();
         mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
-        int taskbarNavThreshold = res.getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
-                ? R.dimen.taskbar_nav_threshold_v2
-                : R.dimen.taskbar_nav_threshold);
+        int taskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
         int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx;
         mTaskbarNavThresholdY = screenHeight - taskbarNavThreshold;
         mIsTaskbarAllAppsOpen =
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 4e80d41..c20f323 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -26,6 +26,7 @@
 import android.util.IntProperty;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.launcher3.util.MultiScalePropertyFactory;
@@ -200,6 +201,23 @@
                 }
             };
 
+    public static final FloatProperty<ImageView> ROTATION_DRAWABLE_PERCENT =
+            new FloatProperty<ImageView>("drawableRotationPercent") {
+                // RotateDrawable linearly interpolates the rotation degrees between fromDegrees
+                // and toDegrees using the drawable level as a percent of its MAX_LEVEL.
+                private static final int MAX_LEVEL = 10000;
+
+                @Override
+                public void setValue(ImageView view, float percent) {
+                    view.setImageLevel((int) (percent * MAX_LEVEL));
+                }
+
+                @Override
+                public Float get(ImageView view) {
+                    return view.getDrawable().getLevel() / (float) MAX_LEVEL;
+                }
+            };
+
     /**
      * Utility method to create an {@link AnimatorListener} which executes a callback on animation
      * cancel.
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index ea0d1b9..9fb0e71 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -477,7 +477,7 @@
         }
         setupHeader();
 
-        if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+        if (isSearchBarOnBottom()) {
             // Keep the scroller above the search bar.
             RelativeLayout.LayoutParams scrollerLayoutParams =
                     (LayoutParams) findViewById(R.id.fast_scroller).getLayoutParams();
@@ -536,7 +536,7 @@
         removeCustomRules(getSearchRecyclerView());
         if (!isSearchSupported()) {
             layoutWithoutSearchContainer(rvContainer, showTabs);
-        } else if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+        } else if (isSearchBarOnBottom()) {
             alignParentTop(rvContainer, showTabs);
             alignParentTop(getSearchRecyclerView(), /* tabs= */ false);
             layoutAboveSearchContainer(rvContainer);
@@ -571,7 +571,7 @@
         removeCustomRules(mHeader);
         if (!isSearchSupported()) {
             layoutWithoutSearchContainer(mHeader, false /* includeTabsMargin */);
-        } else if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+        } else if (isSearchBarOnBottom()) {
             alignParentTop(mHeader, false /* includeTabsMargin */);
         } else {
             layoutBelowSearchContainer(mHeader, false /* includeTabsMargin */);
@@ -610,6 +610,19 @@
                 (int) (mSearchContainer.getAlpha() * 255));
     }
 
+    /**
+     * It is up to the search container view created by {@link #inflateSearchBox()} to use the
+     * floating search bar flag to move itself to the bottom of this container. This method checks
+     * if that had been done; otherwise the flag will be ignored.
+     *
+     * @return true if the search bar is at the bottom of the container (as opposed to the top).
+     **/
+    private boolean isSearchBarOnBottom() {
+        return FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()
+                && ((RelativeLayout.LayoutParams) mSearchContainer.getLayoutParams()).getRule(
+                ALIGN_PARENT_BOTTOM) == RelativeLayout.TRUE;
+    }
+
     private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) {
         if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
             return;
@@ -908,7 +921,7 @@
             setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
         } else {
             int topPadding = grid.allAppsTopPadding;
-            if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() && !grid.isTablet) {
+            if (isSearchBarOnBottom() && !grid.isTablet) {
                 topPadding += getResources().getDimensionPixelSize(
                         R.dimen.all_apps_additional_top_padding_floating_search);
             }
@@ -1109,7 +1122,7 @@
         FloatingHeaderView headerView = getFloatingHeaderView();
         if (isTablet) {
             // Start adding header protection if search bar or tabs will attach to the top.
-            if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
+            if (!isSearchBarOnBottom() || mUsingTabs) {
                 View panel = (View) mBottomSheetBackground;
                 float translationY = ((View) panel.getParent()).getTranslationY();
                 mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY, panel.getRight(),
@@ -1151,7 +1164,7 @@
     /** Returns the position of the bottom edge of the header */
     public int getHeaderBottom() {
         int bottom = (int) getTranslationY() + mHeader.getClipTop();
-        if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+        if (isSearchBarOnBottom()) {
             if (mActivityContext.getDeviceProfile().isTablet) {
                 return bottom + mBottomSheetBackground.getTop();
             }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 9587447..ff0a867 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -319,6 +319,9 @@
     public static final BooleanFlag POPUP_MATERIAL_U = new DeviceFlag(
             "POPUP_MATERIAL_U", false, "Switch popup UX to use material U");
 
+    public static final BooleanFlag ENABLE_SEARCH_UNINSTALLED_APPS = new DeviceFlag(
+            "ENABLE_SEARCH_UNINSTALLED_APPS", false, "Search uninstalled app results.");
+
     public static final BooleanFlag SHOW_HOME_GARDENING = getDebugFlag(
             "SHOW_HOME_GARDENING", false,
             "Show the new home gardening mode");
@@ -335,10 +338,6 @@
             "ENABLE_DOWNLOAD_APP_UX_V3", false, "Updates the download app UX"
             + " to have better visuals, improve contrast, and color");
 
-    public static final BooleanFlag ENABLE_TASKBAR_REVISED_THRESHOLDS = getDebugFlag(
-            "ENABLE_TASKBAR_REVISED_THRESHOLDS", true,
-            "Uses revised thresholds for transient taskbar.");
-
     public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(
             "FORCE_PERSISTENT_TASKBAR", false, "Forces taskbar to be persistent, even in gesture"
                     + " nav mode and when transient taskbar is enabled.");
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 9a34478..d31a646 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -176,7 +176,7 @@
                 MAIN_EXECUTOR.submit(() ->
                         Launcher.ACTIVITY_TRACKER.getCreatedActivity().getRotationHelper()
                                 .forceAllowRotationForTesting(Boolean.parseBoolean(arg)));
-                return null;
+                return response;
 
             case TestProtocol.REQUEST_WORKSPACE_CELL_LAYOUT_SIZE:
                 return getLauncherUIProperty(Bundle::putIntArray, launcher -> {
@@ -232,6 +232,13 @@
                         l -> l.getAppsView().getActiveRecyclerView().getClipBounds().top);
             }
 
+            case TestProtocol.REQUEST_ALL_APPS_BOTTOM_PADDING: {
+                return getLauncherUIProperty(Bundle::putInt,
+                        l -> l.getAppsView().getBottom()
+                                - l.getAppsView().getActiveRecyclerView().getBottom()
+                                + l.getAppsView().getActiveRecyclerView().getPaddingBottom());
+            }
+
             default:
                 return null;
         }
diff --git a/tests/Android.bp b/tests/Android.bp
index 39bd307..7144d65 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -117,3 +117,15 @@
     test_config: "Launcher3Tests.xml",
     data: [":Launcher3"]
 }
+
+// Shared between tests and launcher
+android_library {
+    name: "launcher-testing-shared",
+    srcs: [
+        "shared/com/android/launcher3/testing/shared/**/*.java"
+    ],
+    resource_dirs: [ ],
+    manifest: "shared/AndroidManifest.xml",
+    sdk_version: "current",
+    min_sdk_version: min_launcher3_sdk_version,
+ }
\ No newline at end of file
diff --git a/tests/shared/AndroidManifest.xml b/tests/shared/AndroidManifest.xml
new file mode 100644
index 0000000..1cd0cb3
--- /dev/null
+++ b/tests/shared/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<manifest package="com.android.launcher3.testing.shared">
+</manifest>
diff --git a/src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java b/tests/shared/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java
similarity index 100%
rename from src/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java
rename to tests/shared/com/android/launcher3/testing/shared/HotseatCellCenterRequest.java
diff --git a/src/com/android/launcher3/testing/shared/ResourceUtils.java b/tests/shared/com/android/launcher3/testing/shared/ResourceUtils.java
similarity index 100%
rename from src/com/android/launcher3/testing/shared/ResourceUtils.java
rename to tests/shared/com/android/launcher3/testing/shared/ResourceUtils.java
diff --git a/src/com/android/launcher3/testing/shared/TestInformationRequest.java b/tests/shared/com/android/launcher3/testing/shared/TestInformationRequest.java
similarity index 100%
rename from src/com/android/launcher3/testing/shared/TestInformationRequest.java
rename to tests/shared/com/android/launcher3/testing/shared/TestInformationRequest.java
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
similarity index 98%
rename from src/com/android/launcher3/testing/shared/TestProtocol.java
rename to tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 9b2ce9a..11363a2 100644
--- a/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -120,6 +120,7 @@
     public static final String REQUEST_TASKBAR_ALL_APPS_TOP_PADDING =
             "taskbar-all-apps-top-padding";
     public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding";
+    public static final String REQUEST_ALL_APPS_BOTTOM_PADDING = "all-apps-bottom-padding";
 
     public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size";
     public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center";
diff --git a/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java b/tests/shared/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
similarity index 100%
rename from src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
rename to tests/shared/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4b04c7e..401c25a4 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -169,9 +169,9 @@
                     flingBackwardY < flingForwardY));
 
             // Test scrolling down to YouTube.
-            assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
+            assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube"));
             // Test scrolling up to Camera.
-            assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
+            assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera"));
             // Test failing to find a non-existing app.
             final AllApps allAppsFinal = allApps;
             expectFail("All apps: could find a non-existing app",
@@ -263,8 +263,7 @@
             assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
             test.executeOnLauncher(launcher -> assertTrue(
                     "Launcher activity is the top activity; expecting another activity to be the "
-                            + "top "
-                            + "one",
+                            + "top one",
                     test.isInLaunchedApp(launcher)));
         } finally {
             allApps.unfreeze();
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 6f6428a..2d4d2cd 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -34,6 +34,9 @@
 
 import com.android.launcher3.testing.shared.TestProtocol;
 
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 import java.util.stream.Collectors;
 
 /**
@@ -102,10 +105,10 @@
                 iconCenter.x, iconCenter.y);
     }
 
-    private boolean iconCenterInRecyclerTopPadding(UiObject2 appListRecycler, UiObject2 icon) {
+    private boolean iconCenterInRecyclerTopPadding(UiObject2 appsListRecycler, UiObject2 icon) {
         final Point iconCenter = icon.getVisibleCenter();
 
-        return iconCenter.y <= mLauncher.getVisibleBounds(appListRecycler).top
+        return iconCenter.y <= mLauncher.getVisibleBounds(appsListRecycler).top
                 + getAppsListRecyclerTopPadding();
     }
 
@@ -137,15 +140,11 @@
                             bottomGestureStartOnScreen)) {
                         mLauncher.scrollToLastVisibleRow(
                                 allAppsContainer,
-                                mLauncher.getObjectsInContainer(allAppsContainer, "icon")
-                                        .stream()
-                                        .filter(icon ->
-                                                mLauncher.getVisibleBounds(icon).top
-                                                        < bottomGestureStartOnScreen)
-                                        .collect(Collectors.toList()),
+                                getBottomVisibleIconBounds(allAppsContainer),
                                 mLauncher.getVisibleBounds(appListRecycler).top
                                         + getAppsListRecyclerTopPadding()
-                                        - mLauncher.getVisibleBounds(allAppsContainer).top);
+                                        - mLauncher.getVisibleBounds(allAppsContainer).top,
+                                getAppsListRecyclerBottomPadding());
                         verifyActiveContainer();
                         final int newScroll = getAllAppsScroll();
                         mLauncher.assertTrue(
@@ -175,6 +174,28 @@
         }
     }
 
+    /** @return visible bounds of the top-most visible icon in the container. */
+    protected Rect getTopVisibleIconBounds(UiObject2 allAppsContainer) {
+        return mLauncher.getVisibleBounds(Collections.min(getVisibleIcons(allAppsContainer),
+                Comparator.comparingInt(i -> mLauncher.getVisibleBounds(i).top)));
+    }
+
+    /** @return visible bounds of the bottom-most visible icon in the container. */
+    protected Rect getBottomVisibleIconBounds(UiObject2 allAppsContainer) {
+        return mLauncher.getVisibleBounds(Collections.max(getVisibleIcons(allAppsContainer),
+                Comparator.comparingInt(i -> mLauncher.getVisibleBounds(i).top)));
+    }
+
+    @NonNull
+    private List<UiObject2> getVisibleIcons(UiObject2 allAppsContainer) {
+        return mLauncher.getObjectsInContainer(allAppsContainer, "icon")
+                .stream()
+                .filter(icon ->
+                        mLauncher.getVisibleBounds(icon).top
+                                < mLauncher.getBottomGestureStartOnScreen())
+                .collect(Collectors.toList());
+    }
+
     /**
      * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
      * sure the icon is visible.
@@ -196,20 +217,23 @@
 
     protected abstract int getAppsListRecyclerTopPadding();
 
+    protected int getAppsListRecyclerBottomPadding() {
+        return mLauncher.getTestInfo(TestProtocol.REQUEST_ALL_APPS_BOTTOM_PADDING)
+                .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     private void scrollBackToBeginning() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to scroll back in all apps")) {
             LauncherInstrumentation.log("Scrolling to the beginning");
             final UiObject2 allAppsContainer = verifyActiveContainer();
-            final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
 
             int attempts = 0;
             final Rect margins = new Rect(
                     /* left= */ 0,
-                    mLauncher.getVisibleBounds(appListRecycler).top
-                            + getAppsListRecyclerTopPadding() + 1,
+                    getTopVisibleIconBounds(allAppsContainer).bottom,
                     /* right= */ 0,
-                    /* bottom= */ 5);
+                    /* bottom= */ getAppsListRecyclerBottomPadding());
 
             for (int scroll = getAllAppsScroll();
                     scroll != 0;
@@ -240,7 +264,7 @@
                 .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
-    private UiObject2 getAppListRecycler(UiObject2 allAppsContainer) {
+    protected UiObject2 getAppListRecycler(UiObject2 allAppsContainer) {
         return mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view");
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 50b03aa..e0c4c19 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -17,15 +17,11 @@
 
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
 
-import android.graphics.Rect;
-
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
 import com.android.launcher3.testing.shared.TestProtocol;
 
-import java.util.Objects;
-
 public class HomeAllApps extends AllApps {
     private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
 
@@ -45,10 +41,8 @@
                      mLauncher.addContextLayer("want to switch from all apps to workspace")) {
             UiObject2 allAppsContainer = verifyActiveContainer();
 
-            final Rect searchBoxBounds = Objects.requireNonNull(
-                    mLauncher.getVisibleBounds(getSearchBox(allAppsContainer)));
-            final int startX = searchBoxBounds.centerX();
-            final int startY = searchBoxBounds.bottom;
+            final int startX = allAppsContainer.getVisibleCenter().x;
+            final int startY = getTopVisibleIconBounds(allAppsContainer).centerY();
             final int endY = mLauncher.getDevice().getDisplayHeight();
             LauncherInstrumentation.log(
                     "switchToWorkspace: startY = " + startY + ", endY = " + endY
@@ -59,7 +53,7 @@
                     startY,
                     startX,
                     endY,
-                    12 /* steps */,
+                    5 /* steps */,
                     NORMAL_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE);
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 2d1c963..52994a5 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -76,8 +76,6 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
@@ -1486,19 +1484,21 @@
     }
 
     void scrollToLastVisibleRow(
-            UiObject2 container, Collection<UiObject2> items, int topPaddingInContainer) {
-        final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
-                Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top));
-
-        final int itemRowCurrentTopOnScreen = getVisibleBounds(lowestItem).top;
+            UiObject2 container, Rect bottomVisibleIconBounds, int topPaddingInContainer,
+            int appsListBottomPadding) {
+        final int itemRowCurrentTopOnScreen = bottomVisibleIconBounds.top;
         final Rect containerRect = getVisibleBounds(container);
         final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer;
         final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop();
 
-        scrollDownByDistance(container, distance);
+        scrollDownByDistance(container, distance, appsListBottomPadding);
     }
 
     void scrollDownByDistance(UiObject2 container, int distance) {
+        scrollDownByDistance(container, distance, 0);
+    }
+
+    void scrollDownByDistance(UiObject2 container, int distance, int bottomPadding) {
         final Rect containerRect = getVisibleBounds(container);
         final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
         scroll(
@@ -1508,7 +1508,7 @@
                         0,
                         containerRect.height() - distance - bottomGestureMarginInContainer,
                         0,
-                        bottomGestureMarginInContainer),
+                        bottomGestureMarginInContainer + bottomPadding),
                 /* steps= */ 10,
                 /* slowDown= */ true);
     }