Merge "Add back gesture support when split selection active" into main
diff --git a/Android.bp b/Android.bp
index b68e1e5..4dddbf6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -136,6 +136,24 @@
     min_sdk_version: min_launcher3_sdk_version,
 }
 
+aconfig_declarations {
+    name: "launcher_flags",
+    package: "com.google.android.platform.launcher.aconfig.flags",
+    srcs: ["launcher.aconfig"],
+}
+
+java_aconfig_library {
+    name: "launcher_flags_lib",
+    aconfig_declarations: "launcher_flags",
+}
+
+java_aconfig_library {
+    name: "launcher_flags_lib_test",
+    aconfig_declarations: "launcher_flags",
+    test: true
+}
+
+
 // Library with all the dependencies for building Launcher3
 android_library {
     name: "Launcher3ResLib",
@@ -167,8 +185,8 @@
 //
 // Build rule for Launcher3 dependencies lib.
 //
-android_library {
-    name: "Launcher3CommonDepsLib",
+java_defaults {
+    name: "Launcher3CommonDepsDefault",
     srcs: ["src_build_config/**/*.java"],
     static_libs: [
         "Launcher3ResLib",
@@ -184,13 +202,35 @@
 }
 
 //
+// Build rule for Launcher3 dependencies lib.
+//
+android_library {
+    name: "Launcher3CommonDepsLib",
+    defaults: ["Launcher3CommonDepsDefault"],
+    static_libs: [
+        "launcher_flags_lib",
+    ],
+}
+
+//
+// Build rule for Launcher3 dependencies lib for test and debug.
+//
+android_library {
+    name: "Launcher3CommonDepsLibDebug",
+    defaults: ["Launcher3CommonDepsDefault"],
+    static_libs: [
+        "launcher_flags_lib_test",
+    ],
+}
+
+//
 // Build rule for Launcher3 app.
 //
 android_app {
     name: "Launcher3",
 
     static_libs: [
-        "Launcher3CommonDepsLib",
+        "Launcher3CommonDepsLibDebug",
     ],
     srcs: [
         ":launcher-src",
diff --git a/launcher.aconfig b/launcher.aconfig
new file mode 100644
index 0000000..cab193c
--- /dev/null
+++ b/launcher.aconfig
@@ -0,0 +1,8 @@
+package: "com.google.android.platform.launcher.aconfig.flags"
+
+flag {
+    name: "enable_expanding_pause_work_button"
+    namespace: "launcher"
+    description: "Expand and collapse pause work button while scrolling."
+    bug: "270390779"
+}
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index 67be0dd..860abc1 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -27,6 +27,8 @@
 
   <string name="nav_handle_long_press_handler_class" translatable="false"></string>
 
+  <string name="assist_utils_class" translatable="false"></string>
+
   <string name="secondary_display_predictions_class" translatable="false">com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl</string>
 
   <string name="taskbar_model_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarModelCallbacksFactory</string>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 0f8de34..993f13e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -51,6 +51,7 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.AssistUtilsBase;
 import com.android.quickstep.views.DesktopTaskView;
 
 import java.io.PrintWriter;
@@ -158,7 +159,7 @@
         switch (buttonType) {
             case BUTTON_HOME:
                 logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
-                startAssistant();
+                onLongPressHome();
                 return true;
             case BUTTON_A11Y:
                 logEvent(LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS);
@@ -307,13 +308,17 @@
         }
     }
 
-    private void startAssistant() {
+    private void onLongPressHome() {
         if (mScreenPinned || !mAssistantLongPressEnabled) {
             return;
         }
-        Bundle args = new Bundle();
-        args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
-        mSystemUiProxy.startAssistant(args);
+        // Attempt to start Assist with AssistUtils, otherwise fall back to SysUi's implementation.
+        if (!AssistUtilsBase.newInstance(mService.getApplicationContext()).tryStartAssistOverride(
+                INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
+            Bundle args = new Bundle();
+            args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+            mSystemUiProxy.startAssistant(args);
+        }
     }
 
     private void showQuickSettings() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
index 4b235a9..6c3f0d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
@@ -30,6 +30,7 @@
 fun DismissView.setup() {
     setup(
         DismissView.Config(
+            dismissViewResId = R.id.dismiss_view,
             targetSizeResId = R.dimen.bubblebar_dismiss_target_size,
             iconSizeResId = R.dimen.bubblebar_dismiss_target_icon_size,
             bottomMarginResId = R.dimen.bubblebar_dismiss_target_bottom_margin,
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ef2d20a..f0308df 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -62,6 +62,7 @@
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.AssistUtilsBase;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -250,6 +251,8 @@
         setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
         setUnfoldAnimationListener(mUnfoldAnimationListener);
         setDesktopTaskListener(mDesktopTaskListener);
+        setAssistantOverridesRequested(
+                AssistUtilsBase.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
     }
 
     /**
@@ -374,6 +377,17 @@
     }
 
     @Override
+    public void setAssistantOverridesRequested(int[] invocationTypes) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.setAssistantOverridesRequested(invocationTypes);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setAssistantOverridesRequested", e);
+            }
+        }
+    }
+
+    @Override
     public void notifyAccessibilityButtonClicked(int displayId) {
         if (mSystemUiProxy != null) {
             try {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 22aca25..cd88894 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -117,6 +117,7 @@
 import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActiveGestureLog.CompoundString;
+import com.android.quickstep.util.AssistUtilsBase;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -280,6 +281,20 @@
             }));
         }
 
+        /**
+         * Sent when the assistant has been invoked with the given type (defined in AssistManager)
+         * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested
+         * was previously called including this invocation type.
+         */
+        @Override
+        public void onAssistantOverrideInvoked(int invocationType) {
+            executeForTouchInteractionService(tis -> {
+                if (!AssistUtilsBase.newInstance(tis).tryStartAssistOverride(invocationType)) {
+                    Log.w(TAG, "Failed to invoke Assist override");
+                }
+            });
+        }
+
         @Override
         public void onNavigationBarSurface(SurfaceControl surface) {
             // TODO: implement
diff --git a/quickstep/src/com/android/quickstep/util/AssistUtilsBase.java b/quickstep/src/com/android/quickstep/util/AssistUtilsBase.java
new file mode 100644
index 0000000..7b27020
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AssistUtilsBase.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+/** Utilities to work with Assistant functionality. */
+public class AssistUtilsBase implements ResourceBasedOverride {
+
+    public AssistUtilsBase() {}
+
+    /** Creates AssistUtils as specified by overrides */
+    public static AssistUtilsBase newInstance(Context context) {
+        return Overrides.getObject(AssistUtilsBase.class, context, R.string.assist_utils_class);
+    }
+
+    /** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */
+    public int[] getSysUiAssistOverrideInvocationTypes() {
+        return new int[0];
+    }
+
+    /**
+     * @return {@code true} if the override was handled, i.e. an assist surface was shown or the
+     * request should be ignored. {@code false} means the caller should start assist another way.
+     */
+    public boolean tryStartAssistOverride(int invocationType) {
+        return false;
+    }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index 9622619..b3d04c6 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.view.View;
 
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.logging.StatsLogManager;
@@ -70,6 +71,9 @@
         MockitoAnnotations.initMocks(this);
         when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
         when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
+        when(mockService.getApplicationContext())
+                .thenReturn(InstrumentationRegistry.getInstrumentation().getTargetContext()
+                        .getApplicationContext());
         when(mockStatsLogManager.logger()).thenReturn(mockStatsLogger);
         when(mockTaskbarControllers.getTaskbarActivityContext())
                 .thenReturn(mockTaskbarActivityContext);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 1aa7ab6..92b598b 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -25,6 +25,8 @@
 
 import android.content.Intent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.launcher3.config.FeatureFlags;
@@ -36,7 +38,10 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+@LargeTest
+@RunWith(AndroidJUnit4.class)
 public class TaplTestsSplitscreen extends AbstractQuickStepTest {
     private static final String CALCULATOR_APP_NAME = "Calculator";
     private static final String CALCULATOR_APP_PACKAGE =
diff --git a/res/values/id.xml b/res/values/id.xml
index 7b812de..872ae2f 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -43,4 +43,6 @@
     <item type="id" name="saved_floating_widget_foreground" />
     <item type="id" name="saved_floating_widget_background" />
 
+    <item type="id" name="dismiss_view" />
+
 </resources>
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 40382b2..542266a 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -26,6 +26,8 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
 
+import static com.google.android.platform.launcher.aconfig.flags.Flags.enableExpandingPauseWorkButton;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -561,7 +563,8 @@
             mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, mPersonalMatcher);
             mAH.get(AdapterHolder.WORK).setup(workRecyclerView, mWorkManager.getMatcher());
             workRecyclerView.setId(R.id.apps_list_view_work);
-            if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) {
+            if (enableExpandingPauseWorkButton()
+                    || FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) {
                 mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener(
                         mWorkManager.newScrollListener());
             }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 9bc2a0a..21520bf 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -230,6 +230,7 @@
     public static final BooleanFlag ENABLE_HIDE_HEADER = getReleaseFlag(270390930,
             "ENABLE_HIDE_HEADER", ENABLED, "Hide header on keyboard before typing in all apps");
 
+    // Aconfig migration complete for ENABLE_EXPANDING_PAUSE_WORK_BUTTON.
     public static final BooleanFlag ENABLE_EXPANDING_PAUSE_WORK_BUTTON = getDebugFlag(270390779,
             "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", DISABLED,
             "Expand and collapse pause work button while scrolling");
diff --git a/tests/Android.bp b/tests/Android.bp
index 5a52440..96ffa76 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -73,6 +73,7 @@
     asset_dirs: ["assets"],
     resource_dirs: ["res"],
     static_libs: [
+        "flag-junit-base",
         "launcher-aosp-tapl",
         "androidx.test.core",
         "androidx.test.runner",
@@ -87,6 +88,7 @@
         "truth-prebuilt",
         "platform-test-rules",
         "testables",
+        "launcher_flags_lib_test",
     ],
     manifest: "AndroidManifest-common.xml",
     platform_apis: true,
@@ -103,7 +105,10 @@
         ":launcher-tests-src",
         ":launcher-non-quickstep-tests-src",
     ],
-    static_libs: ["Launcher3TestLib"],
+    static_libs: [
+        "Launcher3TestLib",
+        "launcher_flags_lib_test",
+    ],
     libs: [
         "android.test.base",
         "android.test.runner",
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 5b9adcd..61cdd17 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -22,10 +22,13 @@
 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
+import static com.google.android.platform.launcher.aconfig.flags.Flags.FLAG_ENABLE_EXPANDING_PAUSE_WORK_BUTTON;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.Log;
 import android.view.View;
 
@@ -44,6 +47,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Objects;
@@ -52,6 +56,7 @@
 public class WorkProfileTest extends AbstractLauncherUiTest {
 
     private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private int mProfileUserId;
     private boolean mWorkProfileSetupSuccessful;
@@ -60,6 +65,7 @@
     @Before
     @Override
     public void setUp() throws Exception {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_EXPANDING_PAUSE_WORK_BUTTON);
         super.setUp();
         String output =
                 mDevice.executeShellCommand(
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 23d09d4..fb08ea4 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -336,4 +336,11 @@
         final Bundle testInfo = mLauncher.getTestInfo(TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS);
         return testInfo == null ? 0 : testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
+
+    /**
+     * Return the QSB UI object on the AllApps screen.
+     * @return the QSB UI object.
+     */
+    @NonNull
+    public abstract Qsb getQsb();
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
index c4744a1..0e0291f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -62,4 +62,10 @@
         return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_APPS_LIST_SCROLL_Y)
                 .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
+
+    @NonNull
+    @Override
+    public TaskbarAllAppsQsb getQsb() {
+        return new TaskbarAllAppsQsb(mLauncher, verifyActiveContainer());
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
index 0931cd4..1692351 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
@@ -22,16 +22,7 @@
  */
 class AllAppsQsb extends Qsb {
 
-    private final UiObject2 mAllAppsContainer;
-
     AllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) {
-        super(launcher);
-        mAllAppsContainer = allAppsContainer;
-        waitForQsbObject();
-    }
-
-    @Override
-    protected UiObject2 waitForQsbObject() {
-        return mLauncher.waitForObjectInContainer(mAllAppsContainer, "search_container_all_apps");
+        super(launcher, allAppsContainer, "search_container_all_apps");
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index a03472a..33c6334 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -117,11 +117,8 @@
         }
     }
 
-    /**
-     * Return the QSB UI object on the AllApps screen.
-     * @return the QSB UI object.
-     */
     @NonNull
+    @Override
     public Qsb getQsb() {
         return new AllAppsQsb(mLauncher, verifyActiveContainer());
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 20d09a1..5385c65 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -22,16 +22,7 @@
  */
 class HomeQsb extends Qsb {
 
-    private final UiObject2 mHotSeat;
-
     HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) {
-        super(launcher);
-        mHotSeat = hotseat;
-        waitForQsbObject();
-    }
-
-    @Override
-    protected UiObject2 waitForQsbObject() {
-        return mLauncher.waitForObjectInContainer(mHotSeat, "search_container_hotseat");
+        super(launcher, hotseat, "search_container_hotseat");
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
index 6bc4f21..7f3f61d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Qsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -30,13 +30,21 @@
     private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
     private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
     protected final LauncherInstrumentation mLauncher;
+    private final UiObject2 mContainer;
+    private final String mQsbResName;
 
-    protected Qsb(LauncherInstrumentation launcher) {
+    protected Qsb(LauncherInstrumentation launcher, UiObject2 container, String qsbResName) {
         mLauncher = launcher;
+        mContainer = container;
+        mQsbResName = qsbResName;
+        waitForQsbObject();
     }
 
     // Waits for the quick search box.
-    protected abstract UiObject2 waitForQsbObject();
+    private UiObject2 waitForQsbObject() {
+        return mLauncher.waitForObjectInContainer(mContainer, mQsbResName);
+    }
+
     /**
      * Launch assistant app by tapping mic icon on qsb.
      */
@@ -79,8 +87,12 @@
             mLauncher.waitForIdle();
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
                     "clicked qsb to open search result page")) {
-                return new SearchResultFromQsb(mLauncher);
+                return createSearchResult();
             }
         }
     }
+
+    protected SearchResultFromQsb createSearchResult() {
+        return new SearchResultFromQsb(mLauncher);
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 80176e9..8c3402f 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -32,7 +32,7 @@
 
     // This particular ID change should happen with caution
     private static final String SEARCH_CONTAINER_RES_ID = "search_results_list_view";
-    private final LauncherInstrumentation mLauncher;
+    protected final LauncherInstrumentation mLauncher;
 
     SearchResultFromQsb(LauncherInstrumentation launcher) {
         mLauncher = launcher;
@@ -49,8 +49,12 @@
     }
 
     /** Find the app from search results with app name. */
-    public Launchable findAppIcon(String appName) {
+    public AppIcon findAppIcon(String appName) {
         UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName));
+        return createAppIcon(icon);
+    }
+
+    protected AppIcon createAppIcon(UiObject2 icon) {
         return new AllAppsAppIcon(mLauncher, icon);
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
new file mode 100644
index 0000000..c267c9e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
@@ -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.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on search result page opened from Taskbar qsb.
+ */
+public class SearchResultFromTaskbarQsb extends SearchResultFromQsb {
+
+    SearchResultFromTaskbarQsb(LauncherInstrumentation launcher) {
+        super(launcher);
+    }
+
+    @Override
+    public TaskbarAppIcon findAppIcon(String appName) {
+        return (TaskbarAppIcon) super.findAppIcon(appName);
+    }
+
+    @Override
+    protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
+        return new TaskbarAppIcon(mLauncher, icon);
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java
new file mode 100644
index 0000000..7cecd3e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java
@@ -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.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on Taskbar AllApp screen qsb.
+ */
+public class TaskbarAllAppsQsb extends Qsb {
+
+    TaskbarAllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) {
+        super(launcher, allAppsContainer, "search_container_all_apps");
+    }
+
+    @Override
+    public SearchResultFromTaskbarQsb showSearchResult() {
+        return (SearchResultFromTaskbarQsb) super.showSearchResult();
+    }
+
+    @Override
+    protected SearchResultFromTaskbarQsb createSearchResult() {
+        return new SearchResultFromTaskbarQsb(mLauncher);
+    }
+}