Merge "Remove unused flag UNFOLDED_WIDGET_PICKER" into main
diff --git a/OWNERS b/OWNERS
index 38963d0..b8aae78 100644
--- a/OWNERS
+++ b/OWNERS
@@ -12,7 +12,6 @@
 jonmiranda@google.com
 alexchau@google.com
 patmanning@google.com
-tsuharesu@google.com
 awickham@google.com
 
 # Launcher workspace eng team
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index af175ce..210cfd0 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -43,6 +43,13 @@
 }
 
 flag {
+    name: "enable_focus_outline"
+    namespace: "launcher"
+    description: "Enables focus states outline for launcher."
+    bug: "310953377"
+}
+
+flag {
     name: "enable_taskbar_no_recreate"
     namespace: "launcher"
     description: "Enables taskbar with no recreation from lifecycle changes of TaskbarActivityContext."
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 43716ab..477cd84 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -22,7 +22,10 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Intent;
 import android.os.Bundle;
+import android.view.View;
 import android.view.WindowInsetsController;
 import android.view.WindowManager;
 
@@ -32,6 +35,7 @@
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.widget.BaseWidgetSheet;
+import com.android.launcher3.widget.WidgetCell;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
@@ -81,6 +85,23 @@
         return mDragLayer;
     }
 
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
+        return v -> {
+            final AppWidgetProviderInfo info =
+                    (v instanceof WidgetCell) ? ((WidgetCell) v).getWidgetItem().widgetInfo : null;
+            if (info == null || info.provider == null) {
+                return;
+            }
+
+            setResult(RESULT_OK, new Intent()
+                    .putExtra(Intent.EXTRA_COMPONENT_NAME, info.provider)
+                    .putExtra(Intent.EXTRA_USER, info.getProfile()));
+
+            finish();
+        };
+    }
+
     private void refreshAndBindWidgets() {
         MODEL_EXECUTOR.execute(() -> {
             LauncherAppState app = LauncherAppState.getInstance(this);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index c482911..ec9f4e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -130,6 +130,8 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         TaskbarActivityContext activityContext = ActivityContext.lookupContext(context);
 
+        setAlpha(0);
+        setVisibility(INVISIBLE);
         mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap);
         mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
         mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index ade8074..b2d1b43 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -23,6 +23,7 @@
 import android.animation.ObjectAnimator
 import android.animation.ValueAnimator
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.graphics.Bitmap
 import android.graphics.Rect
 import android.graphics.RectF
@@ -31,9 +32,11 @@
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
 import android.view.View
-import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
 import android.window.TransitionInfo.Change
+import android.window.WindowContainerToken
 import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators
 import com.android.launcher3.DeviceProfile
@@ -387,14 +390,7 @@
                 "trying to launch an app pair icon, but encountered an unexpected null"
             }
 
-            composeIconSplitLaunchAnimator(
-                launchingIconView,
-                initialTaskId,
-                secondTaskId,
-                info,
-                t,
-                finishCallback
-            )
+            composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
         } else {
             // Fallback case: simple fade-in animation
             check(info != null && t != null) {
@@ -461,12 +457,27 @@
     /**
      * When the user taps an app pair icon to launch split, this will play the tasks' launch
      * animation from the position of the icon.
+     *
+     * To find the root shell leash that we want to fade in, we do the following:
+     * The Changes we receive in transitionInfo are structured like this
+     *
+     *     Root (grandparent)
+     *     |
+     *     |--> Split Root 1 (left/top side parent) (WINDOWING_MODE_MULTI_WINDOW)
+     *     |   |
+     *     |    --> App 1 (left/top side child) (WINDOWING_MODE_MULTI_WINDOW)
+     *     |--> Divider
+     *     |--> Split Root 2 (right/bottom side parent) (WINDOWING_MODE_MULTI_WINDOW)
+     *         |
+     *          --> App 2 (right/bottom side child) (WINDOWING_MODE_MULTI_WINDOW)
+     *
+     * We want to animate the Root (grandparent) so that it affects both apps and the divider.
+     * To do this, we find one of the nodes with WINDOWING_MODE_MULTI_WINDOW (one of the
+     * left-side ones, for simplicity) and traverse the tree until we find the grandparent.
      */
     @VisibleForTesting
     fun composeIconSplitLaunchAnimator(
         launchingIconView: AppPairIcon,
-        initialTaskId: Int,
-        secondTaskId: Int,
         transitionInfo: TransitionInfo,
         t: Transaction,
         finishCallback: Runnable
@@ -481,46 +492,47 @@
         progressUpdater.setDuration(timings.getDuration().toLong())
         progressUpdater.interpolator = Interpolators.LINEAR
 
-        // Find the root shell leash that we want to fade in (parent of both app windows and
-        // the divider). For simplicity, we search using the initialTaskId.
-        var rootShellLayer: SurfaceControl? = null
-        var dividerPos = 0
+        var rootCandidate: Change? = null
 
         for (change in transitionInfo.changes) {
             val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
-            val taskId = taskInfo.taskId
-            val mode = change.mode
 
-            if (taskId == initialTaskId || taskId == secondTaskId) {
-                check(
-                    mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT
-                ) {
-                    "Expected task to be showing, but it is $mode"
+            // TODO (b/316490565): Replace this logic when SplitBounds is available to
+            //  startAnimation() and we can know the precise taskIds of launching tasks.
+            // Find a change that has WINDOWING_MODE_MULTI_WINDOW.
+            if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW &&
+                (change.mode == TRANSIT_OPEN || change.mode == TRANSIT_TO_FRONT)) {
+                // Check if it is a left/top app.
+                val isLeftTopApp =
+                    (dp.isLeftRightSplit && change.endAbsBounds.left == 0) ||
+                        (!dp.isLeftRightSplit && change.endAbsBounds.top == 0)
+                if (isLeftTopApp) {
+                    // Found one!
+                    rootCandidate = change
+                    break
                 }
             }
-
-            if (taskId == initialTaskId) {
-                var splitRoot1 = change
-                val parentToken = change.parent
-                if (parentToken != null) {
-                    splitRoot1 = transitionInfo.getChange(parentToken) ?: change
-                }
-
-                val topLevelToken = splitRoot1.parent
-                if (topLevelToken != null) {
-                    rootShellLayer = transitionInfo.getChange(topLevelToken)?.leash
-                }
-
-                dividerPos =
-                    if (dp.isLeftRightSplit) change.endAbsBounds.right
-                    else change.endAbsBounds.bottom
-            }
         }
 
-        check(rootShellLayer != null) {
-            "Could not find a TransitionInfo.Change matching the initialTaskId"
+        // If we could not find a proper root candidate, something went wrong.
+        check(rootCandidate != null) { "Could not find a split root candidate" }
+
+        // Find the place where our left/top app window meets the divider (used for the
+        // launcher side animation)
+        val dividerPos =
+            if (dp.isLeftRightSplit) rootCandidate.endAbsBounds.right
+            else rootCandidate.endAbsBounds.bottom
+
+        // Recurse up the tree until parent is null, then we've found our root.
+        var parentToken: WindowContainerToken? = rootCandidate.parent
+        while (parentToken != null) {
+            rootCandidate = transitionInfo.getChange(parentToken) ?: break
+            parentToken = rootCandidate.parent
         }
 
+        // Make sure nothing weird happened, like getChange() returning null.
+        check(rootCandidate != null) { "Failed to find a root leash" }
+
         // Shell animation: the apps are revealed toward end of the launch animation
         progressUpdater.addUpdateListener { valueAnimator: ValueAnimator ->
             val progress =
@@ -532,7 +544,7 @@
                 )
 
             // Set the alpha of the shell layer (2 apps + divider)
-            t.setAlpha(rootShellLayer, progress)
+            t.setAlpha(rootCandidate.leash, progress)
             t.apply()
         }
 
@@ -651,9 +663,7 @@
             // Find the target tasks' root tasks since those are the split stages that need to
             // be animated (the tasks themselves are children and thus inherit animation).
             if (taskId == initialTaskId || taskId == secondTaskId) {
-                check(
-                    mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT
-                ) {
+                check(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
                     "Expected task to be showing, but it is $mode"
                 }
             }
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 8d54dce..39633ea 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -218,10 +218,17 @@
     }
 
     private BaseOverview pressHomeAndGoToOverview() {
-        mDevice.pressHome();
+        pressHomeAndWaitForOverviewClose();
         return mLauncher.getLaunchedAppState().switchToOverview();
     }
 
+    private void pressHomeAndWaitForOverviewClose() {
+        mDevice.pressHome();
+        Wait.atMost("Recents activity didn't stop",
+                () -> getFromRecents(recents -> !recents.isStarted()),
+                DEFAULT_UI_TIMEOUT, mLauncher);
+    }
+
     // b/143488140
     //@NavigationModeSwitch
     @Test
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 6cbe171..25adb62 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -358,14 +358,11 @@
         // Debug if we need to goHome to prevent wrong previous state b/315525621
         mLauncher.goHome();
         assumeFalse(FeatureFlags.ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION.get());
-        mLauncher.getWorkspace().switchToAllApps();
-        mLauncher.pressBack();
-        mLauncher.getWorkspace();
+        mLauncher.getWorkspace().switchToAllApps().pressBackToWorkspace();
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
 
         startAppFast(CALCULATOR_APP_PACKAGE);
-        mLauncher.pressBack();
-        mLauncher.getWorkspace();
+        mLauncher.getLaunchedAppState().pressBackToWorkspace();
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index 86018b1..929bd8e 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -249,7 +249,7 @@
         val spySplitAnimationController = spy(splitAnimationController)
         doNothing()
             .whenever(spySplitAnimationController)
-            .composeIconSplitLaunchAnimator(any(), any(), any(), any(), any(), any())
+            .composeIconSplitLaunchAnimator(any(), any(), any(), any())
 
         spySplitAnimationController.playSplitLaunchAnimation(
             null /* launchingTaskView */,
@@ -267,7 +267,7 @@
         )
 
         verify(spySplitAnimationController)
-            .composeIconSplitLaunchAnimator(any(), any(), any(), any(), any(), any())
+            .composeIconSplitLaunchAnimator(any(), any(), any(), any())
     }
 
     @Test
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 4093744..6af346e 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -19,7 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:focusable="true" >
+    android:focusable="true"
+    android:defaultFocusHighlightEnabled="false">
     <com.android.launcher3.views.DoubleShadowBubbleTextView
         style="@style/BaseIcon.Workspace"
         android:id="@+id/folder_icon_name"
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 6c3b54c..53d7d0c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -25,6 +25,7 @@
     <color name="uninstall_target_hover_tint">#FFF0592B</color>
 
     <color name="focused_background">#80c6c5c5</color>
+    <color name="focus_outline_color">@color/material_color_on_secondary_container</color>
 
     <color name="default_shadow_color_no_alpha">#FF000000</color>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0a57127..62ce256 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -436,6 +436,9 @@
     <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
     <dimen name="split_instructions_start_margin_cancel">8dp</dimen>
 
+    <dimen name="focus_outline_radius">16dp</dimen>
+    <dimen name="focus_outline_stroke_width">3dp</dimen>
+
     <!-- Workspace grid visualization parameters -->
     <dimen name="grid_visualization_rounding_radius">16dp</dimen>
     <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index 83003ff..1a7d797 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -20,6 +20,7 @@
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 
 /**
@@ -29,7 +30,8 @@
         implements OnFocusChangeListener {
 
     public FocusIndicatorHelper(View container) {
-        super(container, container.getResources().getColor(R.color.focused_background));
+        super(container, container.getResources().getColor(Flags.enableFocusOutline()
+                ? R.color.focus_outline_color : R.color.focused_background));
     }
 
     @Override
@@ -53,7 +55,18 @@
 
         @Override
         public void viewToRect(View v, Rect outRect) {
-            outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+            if (Flags.enableFocusOutline()) {
+                // Ensure the left and top would not be negative and drawn outside of canvas
+                outRect.set(Math.max(0, v.getLeft()), Math.max(0, v.getTop()), v.getRight(),
+                        v.getBottom());
+                // Stroke is drawn with half outside and half inside the view. Inset by half
+                // stroke width to move the whole stroke inside the view and avoid other views
+                // occluding it
+                int halfStrokeWidth = (int) mPaint.getStrokeWidth() / 2;
+                outRect.inset(halfStrokeWidth, halfStrokeWidth);
+            } else {
+                outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
index 2dc8d81..8eb5c7d 100644
--- a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -29,6 +29,7 @@
 import android.util.FloatProperty;
 import android.view.View;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 
 /**
@@ -97,13 +98,22 @@
         mContainer = container;
 
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mMaxAlpha = Color.alpha(color);
         mPaint.setColor(0xFF000000 | color);
+        if (Flags.enableFocusOutline()) {
+            mPaint.setStyle(Paint.Style.STROKE);
+            mPaint.setStrokeWidth(container.getResources().getDimensionPixelSize(
+                    R.dimen.focus_outline_stroke_width));
+            mRadius = container.getResources().getDimensionPixelSize(
+                    R.dimen.focus_outline_radius);
+        } else {
+            mPaint.setStyle(Paint.Style.FILL);
+            mRadius = container.getResources().getDimensionPixelSize(
+                    R.dimen.grid_visualization_rounding_radius);
+        }
+        mMaxAlpha = Color.alpha(color);
 
         setAlpha(0);
         mShift = 0;
-        mRadius = container.getResources().getDimensionPixelSize(
-                R.dimen.grid_visualization_rounding_radius);
     }
 
     protected void setAlpha(float alpha) {
diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
index cbc6f44..71957e1 100644
--- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
+++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
@@ -62,7 +62,7 @@
                 override fun getLayoutManager(): RecyclerView.LayoutManager? = null
             }
 
-        executorRunnable?.cancel(/* interrupt= */ true)
+        executorRunnable?.cancel(/* interrupt= */ false)
         executorRunnable =
             ExecutorRunnable.createAndExecute(
                 VIEW_PREINFLATION_EXECUTOR,
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index c30342a..8f5e2b6 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -147,6 +147,11 @@
         return mAppWidgetHostViewScale;
     }
 
+    /** Returns the {@link WidgetItem} for this {@link WidgetCell}. */
+    public WidgetItem getWidgetItem() {
+        return mItem;
+    }
+
     /**
      * Called to clear the view and free attached resources. (e.g., {@link Bitmap}
      */
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index e46726d..d44ccf5 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -17,11 +17,13 @@
 
 import android.content.Context
 import android.content.res.Configuration
+import android.content.res.Resources
 import android.graphics.Point
 import android.graphics.Rect
 import android.util.DisplayMetrics
 import android.view.Surface
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.testing.shared.ResourceUtils
 import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
@@ -30,6 +32,8 @@
 import com.android.launcher3.util.rule.TestStabilityRule
 import com.android.launcher3.util.window.CachedDisplayInfo
 import com.android.launcher3.util.window.WindowManagerProxy
+import com.android.wm.shell.Flags
+import com.google.common.truth.Truth
 import java.io.BufferedReader
 import java.io.File
 import java.io.PrintWriter
@@ -49,11 +53,18 @@
  * For an implementation that mocks InvariantDeviceProfile, use [FakeInvariantDeviceProfileTest]
  */
 abstract class AbstractDeviceProfileTest {
+    protected val testContext: Context = InstrumentationRegistry.getInstrumentation().context
     protected lateinit var context: SandboxContext
     protected open val runningContext: Context = ApplicationProvider.getApplicationContext()
     private val displayController: DisplayController = mock()
     private val windowManagerProxy: WindowManagerProxy = mock()
     private val launcherPrefs: LauncherPrefs = mock()
+    private val allowLeftRightSplitInPortrait: Boolean = initAllowLeftRightSplitInPortrait()
+    fun initAllowLeftRightSplitInPortrait() : Boolean {
+        val res = Resources.getSystem();
+        val resId = res.getIdentifier("config_leftRightSplitInPortrait", "bool", "android")
+        return Flags.enableLeftRightSplitInPortrait() && resId > 0 && res.getBoolean(resId)
+    }
 
     @Rule @JvmField val testStabilityRule = TestStabilityRule()
 
@@ -306,6 +317,25 @@
         whenever(info.isTransientTaskbar).thenReturn(isGestureMode)
     }
 
+    /** Asserts that the given device profile matches a previously dumped device profile state. */
+    protected fun assertDump(dp: DeviceProfile, folderName: String, filename: String) {
+        val dump = dump(context!!, dp, "${folderName}_$filename.txt")
+        var expected = readDumpFromAssets(testContext, "$folderName/$filename.txt")
+
+        // TODO(b/315230497): We don't currently have device-specific device profile dumps, so just
+        //  update the result before we do the comparison
+        if (allowLeftRightSplitInPortrait) {
+            val isLeftRightSplitInPortrait = when {
+                allowLeftRightSplitInPortrait && dp.isTablet -> !dp.isLandscape
+                else -> dp.isLandscape
+            }
+            expected = expected.replace(Regex("isLeftRightSplit:\\w+"),
+                    "isLeftRightSplit:$isLeftRightSplitInPortrait")
+        }
+
+        Truth.assertThat(dump).isEqualTo(expected)
+    }
+
     /** Create a new dump of DeviceProfile, saves to a file in the device and returns it */
     protected fun dump(context: Context, dp: DeviceProfile, fileName: String): String {
         val stringWriter = StringWriter()
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java
index b4a5169..4f10287 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllApps.java
@@ -214,8 +214,7 @@
                 .pressBackToWorkspace();
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
-        mLauncher.pressBack();
-        mLauncher.getWorkspace();
+        mLauncher.getLaunchedAppState().pressBackToWorkspace();
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
     }
 
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index 9b67310..9409ac1 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -30,7 +30,6 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
-    private val testContext: Context = InstrumentationRegistry.getInstrumentation().context
     private val folderName: String = "DeviceProfileDumpTest"
     @Test
     fun phonePortrait3Button() {
@@ -154,9 +153,6 @@
     }
 
     private fun assertDump(dp: DeviceProfile, filename: String) {
-        val dump = dump(context!!, dp, "${folderName}_$filename.txt")
-        val expected = readDumpFromAssets(testContext, "$folderName/$filename.txt")
-
-        assertThat(dump).isEqualTo(expected)
+        assertDump(dp, folderName, filename);
     }
 }
diff --git a/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt b/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
index b4591ba..b8d74aa 100644
--- a/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
+++ b/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
@@ -18,6 +18,7 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.launcher3.util.rule.TestStabilityRule
 import java.util.concurrent.ExecutorService
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
@@ -65,8 +66,11 @@
     }
 
     @Test
+    @TestStabilityRule.Stability(
+        flavors = TestStabilityRule.LOCAL or TestStabilityRule.PLATFORM_POSTSUBMIT
+    ) // b/316588649
     fun run_and_cancel_cancelCallback() {
-        underTest.cancel(true)
+        underTest.cancel(false)
         awaitAllExecutorCompleted()
 
         assertFalse(isCallbackExecuted)
@@ -77,7 +81,7 @@
     fun run_and_cancelAfterCompletion_executeAll() {
         awaitAllExecutorCompleted()
 
-        underTest.cancel(true)
+        underTest.cancel(false)
 
         assertTrue(isTaskExecuted)
         assertTrue(isCallbackExecuted)
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
index a1d8059..5ef82ca 100644
--- a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
@@ -58,15 +58,15 @@
 
     private final LauncherInstrumentation mLauncher;
     private final LauncherInstrumentation.ContainerType mStartingContainerType;
-    private final boolean mExpectHomeKeyEventsOnDismiss;
+    private final boolean mIsHomeState;
 
     KeyboardQuickSwitch(
             LauncherInstrumentation launcher,
             LauncherInstrumentation.ContainerType startingContainerType,
-            boolean expectHomeKeyEventsOnDismiss) {
+            boolean isHomeState) {
         mLauncher = launcher;
         mStartingContainerType = startingContainerType;
-        mExpectHomeKeyEventsOnDismiss = expectHomeKeyEventsOnDismiss;
+        mIsHomeState = isHomeState;
     }
 
     /**
@@ -164,7 +164,7 @@
                 mLauncher.verifyContainerType(mStartingContainerType);
 
                 // Wait until the device has fully settled before unpressing the key code
-                if (mExpectHomeKeyEventsOnDismiss) {
+                if (mIsHomeState) {
                     mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_HOME_ALT_LEFT_UP);
                 }
                 mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
@@ -204,7 +204,14 @@
                 "want to launch focused task: "
                         + (expectedPackageName == null ? "Overview" : expectedPackageName))) {
             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KQS_ALT_LEFT_UP);
-            mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
+
+            if (expectedPackageName == null || !mIsHomeState) {
+                mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
+            } else {
+                mLauncher.executeAndWaitForLauncherStop(
+                        () -> mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0),
+                        "unpressing left alt");
+            }
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
                     "un-pressed left alt")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 184ece7..501c4c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -346,10 +346,14 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to press back from launched app to workspace")) {
-            mLauncher.executeAndWaitForWallpaperAnimation(
-                    () -> mLauncher.pressBackImpl(),
-                    "pressing back"
-            );
+            if (mLauncher.isLauncher3()) {
+                mLauncher.pressBackImpl();
+            } else {
+                mLauncher.executeAndWaitForWallpaperAnimation(
+                        () -> mLauncher.pressBackImpl(),
+                        "pressing back"
+                );
+            }
             return new Workspace(mLauncher);
         }
     }