Merge "Animate the position of the selected bubble arrow in the expanded bubble bar." into udc-dev
diff --git a/quickstep/res/drawable-sw600dp-land/gesture_tutorial_home_step_shape.xml b/quickstep/res/drawable-sw600dp-land/gesture_tutorial_home_step_shape.xml
index 4cccd09..fd14d34 100644
--- a/quickstep/res/drawable-sw600dp-land/gesture_tutorial_home_step_shape.xml
+++ b/quickstep/res/drawable-sw600dp-land/gesture_tutorial_home_step_shape.xml
@@ -17,11 +17,7 @@
     android:height="67dp"
     android:viewportWidth="232"
     android:viewportHeight="67">
-  <group>
-    <clip-path
-        android:pathData="M0,0h232v67h-232z"/>
-    <path
-        android:pathData="M180.9,0.6H51.1C22.9,0.6 0,23.4 0,51.7V67.6H232V51.7C232,23.4 209.1,0.6 180.9,0.6Z"
-        android:fillColor="#4B67AE"/>
-  </group>
+  <path
+      android:pathData="M180.9,0.6H51.1C22.9,0.6 0,23.4 0,51.7V67.6H232V51.7C232,23.4 209.1,0.6 180.9,0.6Z"
+      android:fillColor="#4B67AE"/>
 </vector>
diff --git a/quickstep/res/drawable-sw600dp-land/gesture_tutorial_overview_step_shape.xml b/quickstep/res/drawable-sw600dp-land/gesture_tutorial_overview_step_shape.xml
index 7011f6c..f271c47 100644
--- a/quickstep/res/drawable-sw600dp-land/gesture_tutorial_overview_step_shape.xml
+++ b/quickstep/res/drawable-sw600dp-land/gesture_tutorial_overview_step_shape.xml
@@ -17,11 +17,7 @@
     android:height="94dp"
     android:viewportWidth="194"
     android:viewportHeight="94">
-  <group>
-    <clip-path
-        android:pathData="M0,0h194v94.09h-194z"/>
-    <path
-        android:pathData="M185.56,76.95C184.79,75.3 184.3,73.56 184.21,71.81L182.85,55.81C182.46,51.34 180.13,47.27 176.45,44.65L163.25,35.44C161.8,34.37 160.54,33.11 159.47,31.65L150.16,18.46C147.54,14.77 143.47,12.45 139,12.06L123,10.6C121.25,10.41 119.51,10.02 117.86,9.24L103.31,2.45C101.27,1.49 99.04,1 96.91,1C94.77,1 92.54,1.49 90.5,2.45L75.95,9.24C74.31,10.02 72.56,10.51 70.81,10.6L54.81,11.96C50.35,12.35 46.27,14.68 43.65,18.36L34.44,31.56C33.37,33.01 32.11,34.27 30.66,35.34L17.27,44.65C13.58,47.27 11.26,51.34 10.87,55.81L9.41,71.81C9.22,73.56 8.83,75.3 8.05,76.95L1.26,91.5C0.78,92.67 0.39,93.83 0.1,94.99H193.52C193.32,93.83 192.94,92.57 192.35,91.5L185.56,76.95Z"
-        android:fillColor="#7E44AD"/>
-  </group>
+  <path
+      android:pathData="M185.56,76.95C184.79,75.3 184.3,73.56 184.21,71.81L182.85,55.81C182.46,51.34 180.13,47.27 176.45,44.65L163.25,35.44C161.8,34.37 160.54,33.11 159.47,31.65L150.16,18.46C147.54,14.77 143.47,12.45 139,12.06L123,10.6C121.25,10.41 119.51,10.02 117.86,9.24L103.31,2.45C101.27,1.49 99.04,1 96.91,1C94.77,1 92.54,1.49 90.5,2.45L75.95,9.24C74.31,10.02 72.56,10.51 70.81,10.6L54.81,11.96C50.35,12.35 46.27,14.68 43.65,18.36L34.44,31.56C33.37,33.01 32.11,34.27 30.66,35.34L17.27,44.65C13.58,47.27 11.26,51.34 10.87,55.81L9.41,71.81C9.22,73.56 8.83,75.3 8.05,76.95L1.26,91.5C0.78,92.67 0.39,93.83 0.1,94.99H193.52C193.32,93.83 192.94,92.57 192.35,91.5L185.56,76.95Z"
+      android:fillColor="#7E44AD"/>
 </vector>
diff --git a/quickstep/res/drawable-sw720dp-land/gesture_tutorial_home_step_shape.xml b/quickstep/res/drawable-sw720dp-land/gesture_tutorial_home_step_shape.xml
index 5becb8b..3e71a3d 100644
--- a/quickstep/res/drawable-sw720dp-land/gesture_tutorial_home_step_shape.xml
+++ b/quickstep/res/drawable-sw720dp-land/gesture_tutorial_home_step_shape.xml
@@ -17,11 +17,7 @@
     android:height="73dp"
     android:viewportWidth="362"
     android:viewportHeight="73">
-  <group>
-    <clip-path
-        android:pathData="M0,0h362v73h-362z"/>
-    <path
-        android:pathData="M282.3,0H79.7C38,0 3.7,32.1 0.3,73H361.7C358.3,32.1 324,0 282.3,0Z"
-        android:fillColor="#4B67AE"/>
-  </group>
+  <path
+      android:pathData="M282.3,0H79.7C38,0 3.7,32.1 0.3,73H361.7C358.3,32.1 324,0 282.3,0Z"
+      android:fillColor="#4B67AE"/>
 </vector>
diff --git a/quickstep/res/drawable-sw720dp-land/gesture_tutorial_overview_step_shape.xml b/quickstep/res/drawable-sw720dp-land/gesture_tutorial_overview_step_shape.xml
index 7143089..2f11192 100644
--- a/quickstep/res/drawable-sw720dp-land/gesture_tutorial_overview_step_shape.xml
+++ b/quickstep/res/drawable-sw720dp-land/gesture_tutorial_overview_step_shape.xml
@@ -17,11 +17,7 @@
     android:height="144dp"
     android:viewportWidth="297"
     android:viewportHeight="144">
-  <group>
-    <clip-path
-        android:pathData="M0,0h297v144.04h-297z"/>
-    <path
-        android:pathData="M284.38,116.48C283.19,113.95 282.45,111.28 282.3,108.61L280.22,84.1C279.63,77.27 276.06,71.03 270.42,67.03L250.22,52.92C247.99,51.28 246.06,49.35 244.43,47.13L230.18,26.93C226.16,21.29 219.93,17.72 213.1,17.13L188.6,14.9C185.92,14.6 183.25,14.01 180.72,12.82L158.45,2.43C155.33,0.94 151.91,0.2 148.65,0.2C145.38,0.2 141.97,0.94 138.85,2.43L116.57,12.82C114.05,14.01 111.38,14.75 108.7,14.9L84.2,16.98C77.37,17.57 71.13,21.14 67.12,26.78L53.01,46.98C51.38,49.21 49.45,51.14 47.22,52.77L26.73,67.03C21.09,71.03 17.52,77.27 16.93,84.1L14.7,108.61C14.4,111.28 13.81,113.95 12.62,116.48L2.23,138.75C1.48,140.53 0.89,142.32 0.45,144.1H296.55C296.26,142.32 295.66,140.38 294.77,138.75L284.38,116.48Z"
-        android:fillColor="#7E44AD"/>
-  </group>
+  <path
+      android:pathData="M284.38,116.48C283.19,113.95 282.45,111.28 282.3,108.61L280.22,84.1C279.63,77.27 276.06,71.03 270.42,67.03L250.22,52.92C247.99,51.28 246.06,49.35 244.43,47.13L230.18,26.93C226.16,21.29 219.93,17.72 213.1,17.13L188.6,14.9C185.92,14.6 183.25,14.01 180.72,12.82L158.45,2.43C155.33,0.94 151.91,0.2 148.65,0.2C145.38,0.2 141.97,0.94 138.85,2.43L116.57,12.82C114.05,14.01 111.38,14.75 108.7,14.9L84.2,16.98C77.37,17.57 71.13,21.14 67.12,26.78L53.01,46.98C51.38,49.21 49.45,51.14 47.22,52.77L26.73,67.03C21.09,71.03 17.52,77.27 16.93,84.1L14.7,108.61C14.4,111.28 13.81,113.95 12.62,116.48L2.23,138.75C1.48,140.53 0.89,142.32 0.45,144.1H296.55C296.26,142.32 295.66,140.38 294.77,138.75L284.38,116.48Z"
+      android:fillColor="#7E44AD"/>
 </vector>
diff --git a/quickstep/res/drawable/gesture_tutorial_back_step_shape.xml b/quickstep/res/drawable/gesture_tutorial_back_step_shape.xml
index 68c5eb1..5f951e4 100644
--- a/quickstep/res/drawable/gesture_tutorial_back_step_shape.xml
+++ b/quickstep/res/drawable/gesture_tutorial_back_step_shape.xml
@@ -17,11 +17,7 @@
     android:height="208dp"
     android:viewportWidth="83"
     android:viewportHeight="208">
-  <group>
-    <clip-path
-        android:pathData="M0,0h83.95v208h-83.95z"/>
-    <path
-        android:pathData="M23.53,169.2L31.09,165.56C76.7,143.55 76.7,64.45 31.09,42.35L23.53,38.71C13.55,33.95 5.06,25.56 -1,14.92V193.08C5.06,182.44 13.55,174.05 23.53,169.2Z"
-        android:fillColor="#217500"/>
-  </group>
+  <path
+      android:pathData="M23.53,169.2L31.09,165.56C76.7,143.55 76.7,64.45 31.09,42.35L23.53,38.71C13.55,33.95 5.06,25.56 -1,14.92V193.08C5.06,182.44 13.55,174.05 23.53,169.2Z"
+      android:fillColor="#217500"/>
 </vector>
diff --git a/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml b/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml
index 39c7e73..d24219d 100644
--- a/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml
+++ b/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml
@@ -28,11 +28,13 @@
         android:layout_width="0dp"
         android:layout_height="@dimen/gesture_tutorial_menu_button_height"
         android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
+        android:layout_marginBottom="24dp"
         android:background="@drawable/gesture_tutorial_menu_button_background"
         android:clipToOutline="true"
         android:backgroundTint="@color/gesture_home_tutorial_background"
 
         app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/guideline"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_back_button">
 
@@ -40,6 +42,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/gesture_tutorial_home_step_shape"
+            android:scaleType="fitXY"
+            android:adjustViewBounds="true"
 
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
@@ -64,11 +68,13 @@
         android:layout_width="0dp"
         android:layout_height="@dimen/gesture_tutorial_menu_button_height"
         android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
+        android:layout_marginBottom="24dp"
         android:background="@drawable/gesture_tutorial_menu_button_background"
         android:clipToOutline="true"
         android:backgroundTint="@color/gesture_back_tutorial_exiting_app"
 
         app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/guideline"
         app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_home_button"
         app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_overview_button">
 
@@ -77,6 +83,8 @@
             android:layout_height="wrap_content"
             android:src="@drawable/gesture_tutorial_back_step_shape"
             android:layout_marginBottom="@dimen/gesture_tutorial_menu_back_shape_bottom_margin"
+            android:scaleType="fitXY"
+            android:adjustViewBounds="true"
 
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"/>
@@ -99,11 +107,13 @@
         android:id="@+id/gesture_tutorial_menu_overview_button"
         android:layout_width="0dp"
         android:layout_height="@dimen/gesture_tutorial_menu_button_height"
+        android:layout_marginBottom="24dp"
         android:background="@drawable/gesture_tutorial_menu_button_background"
         android:clipToOutline="true"
         android:backgroundTint="@color/gesture_overview_tutorial_background"
 
         app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/guideline"
         app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_back_button"
         app:layout_constraintEnd_toEndOf="parent">
 
@@ -111,6 +121,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/gesture_tutorial_overview_step_shape"
+            android:scaleType="fitXY"
+            android:adjustViewBounds="true"
 
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
@@ -142,10 +154,8 @@
         style="@style/TextAppearance.GestureTutorial.ButtonLabel"
         android:id="@+id/gesture_tutorial_menu_done_button"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingVertical="16dp"
-        android:paddingHorizontal="26dp"
-        android:layout_marginVertical="@dimen/gesture_tutorial_menu_done_button_margin"
+        android:layout_height="40dp"
+        android:layout_marginVertical="16dp"
         android:text="@string/gesture_tutorial_action_button_label"
         android:background="@drawable/gesture_tutorial_action_button_background"
         android:stateListAnimator="@null"
diff --git a/quickstep/res/layout/gesture_tutorial_step_menu.xml b/quickstep/res/layout/gesture_tutorial_step_menu.xml
index 2836259..cf1e4d7 100644
--- a/quickstep/res/layout/gesture_tutorial_step_menu.xml
+++ b/quickstep/res/layout/gesture_tutorial_step_menu.xml
@@ -42,6 +42,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/gesture_tutorial_home_step_shape"
+            android:scaleType="fitXY"
+            android:adjustViewBounds="true"
 
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
@@ -79,6 +81,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/gesture_tutorial_back_step_shape"
+            android:scaleType="fitXY"
+            android:adjustViewBounds="true"
 
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
@@ -116,6 +120,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:src="@drawable/gesture_tutorial_overview_step_shape"
+            android:scaleType="fitXY"
+            android:adjustViewBounds="true"
 
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
@@ -147,9 +153,8 @@
         style="@style/TextAppearance.GestureTutorial.ButtonLabel"
         android:id="@+id/gesture_tutorial_menu_done_button"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingVertical="16dp"
-        android:paddingHorizontal="26dp"
+        android:layout_height="40dp"
+        android:layout_marginVertical="16dp"
         android:text="@string/gesture_tutorial_action_button_label"
         android:background="@drawable/gesture_tutorial_action_button_background"
         android:stateListAnimator="@null"
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 9853140..9cb3fec 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -26,6 +26,5 @@
     <dimen name="gesture_tutorial_menu_button_spacing">24dp</dimen>
     <dimen name="gesture_tutorial_menu_done_button_top_spacing">40dp</dimen>
     <dimen name="gesture_tutorial_menu_back_shape_bottom_margin">49dp</dimen>
-    <dimen name="gesture_tutorial_menu_done_button_margin">16dp</dimen>
 
 </resources>
diff --git a/quickstep/res/values-sw720dp-land/dimens.xml b/quickstep/res/values-sw720dp-land/dimens.xml
index 1d02ab5..4634a2d 100644
--- a/quickstep/res/values-sw720dp-land/dimens.xml
+++ b/quickstep/res/values-sw720dp-land/dimens.xml
@@ -21,7 +21,5 @@
     <dimen name="gesture_tutorial_menu_button_spacing">49dp</dimen>
     <dimen name="gesture_tutorial_menu_done_button_top_spacing">24dp</dimen>
     <dimen name="gesture_tutorial_menu_back_shape_bottom_margin">21dp</dimen>
-    <dimen name="gesture_tutorial_menu_done_button_spacing">80dp</dimen>
-    <dimen name="gesture_tutorial_menu_done_button_margin">0dp</dimen>
 
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 5d2df70..d69b155 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -134,7 +134,6 @@
     <dimen name="gesture_tutorial_menu_done_button_top_spacing">0dp</dimen>
     <dimen name="gesture_tutorial_menu_back_shape_bottom_margin">0dp</dimen>
     <dimen name="gesture_tutorial_menu_done_button_spacing">72dp</dimen>
-    <dimen name="gesture_tutorial_menu_done_button_margin">0dp</dimen>
 
     <!-- Gesture Tutorial mock conversations -->
     <dimen name="gesture_tutorial_message_icon_size">44dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 977163b..682fccd 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1448,7 +1448,6 @@
      */
     private Animator getFallbackClosingWindowAnimators(RemoteAnimationTarget[] appTargets) {
         final int rotationChange = getRotationChange(appTargets);
-        SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
         Matrix matrix = new Matrix();
         Point tmpPos = new Point();
         Rect tmpRect = new Rect();
@@ -1504,7 +1503,7 @@
                                 .setAlpha(1f);
                     }
                 }
-                surfaceApplier.scheduleApply(transaction);
+                transaction.getTransaction().apply();
             }
         });
 
@@ -1592,7 +1591,8 @@
             boolean playFallBackAnimation = (launcherView == null
                     && launcherIsForceInvisibleOrOpening)
                     || mLauncher.getWorkspace().isOverlayShown()
-                    || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
+                    || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING)
+                    || mLauncher.isDestroyed();
 
             boolean playWorkspaceReveal = true;
             boolean skipAllAppsScale = false;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 5abeac7..a442849 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -176,9 +176,9 @@
                     deepShortcutCount,
                     mPopupDataProvider.getNotificationKeysForItem(item),
                     systemShortcuts);
-            icon.clearAccessibilityFocus();
         }
 
+        icon.clearAccessibilityFocus();
         container.addOnAttachStateChangeListener(
                 new PopupLiveUpdateHandler<BaseTaskbarContext>(context, container) {
                     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
index b1633e7..3cd5f75 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
@@ -30,7 +30,5 @@
     val appName: String
 ) {
 
-    fun getKey(): String {
-        return info.key
-    }
+    val key: String = info.key
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index a466548..6d19692 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -360,7 +360,7 @@
         Path dotPath;
         int dotColor;
 
-        boolean isImportantConvo = false; // TODO: (b/269671451) needs to be added to BubbleInfo
+        boolean isImportantConvo = b.isImportantConversation();
 
         ShortcutRequest.QueryResult result = new ShortcutRequest(context,
                 new UserHandle(b.getUserId()))
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 84a5228..5902912 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -92,6 +92,11 @@
     }
 
     @Override
+    public View.AccessibilityDelegate getAccessibilityDelegate() {
+        return mTaskbarContext.getAccessibilityDelegate();
+    }
+
+    @Override
     public TaskbarDragController getDragController() {
         return mDragController;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 1ac0742..aeac760 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -186,7 +186,8 @@
     public void launchTutorialMenu() {
         mFragment = new MenuFragment();
         getSupportFragmentManager().beginTransaction()
-                .add(R.id.gesture_tutorial_fragment_container, mFragment)
+                .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+                .runOnCommit(() -> mFragment.onAttachedToWindow())
                 .commit();
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/MenuFragment.java b/quickstep/src/com/android/quickstep/interaction/MenuFragment.java
index ccff30d..46f79b1 100644
--- a/quickstep/src/com/android/quickstep/interaction/MenuFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/MenuFragment.java
@@ -19,6 +19,7 @@
 import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_TUTORIAL_TYPE;
 import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_TUTORIAL_MENU;
 
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -27,17 +28,33 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 
 /** Displays the gesture nav tutorial menu. */
 public final class MenuFragment extends GestureSandboxFragment {
 
+    @NonNull private Rect mInsets = new Rect();
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mInsets = InvariantDeviceProfile.INSTANCE.get(getContext())
+                .getDeviceProfile(getContext()).getInsets();
+    }
+
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
             @Nullable Bundle savedInstanceState) {
         View root = inflater.inflate(
                 R.layout.gesture_tutorial_step_menu, container, false);
 
+        root.setPadding(
+                root.getPaddingLeft() + mInsets.left,
+                root.getPaddingTop() + mInsets.top,
+                root.getPaddingRight() + mInsets.right,
+                root.getPaddingBottom() + mInsets.bottom);
+
         root.findViewById(R.id.gesture_tutorial_menu_home_button).setOnClickListener(
                 v -> launchTutorialStep(TutorialController.TutorialType.HOME_NAVIGATION));
         root.findViewById(R.id.gesture_tutorial_menu_back_button).setOnClickListener(
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 298d49a..fbb8109 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -494,6 +494,7 @@
         private long mLatencyInMillis;
         private int mQueryLength = -1;
         private int mSubEventType = 0;
+        private int mCardinality = -1;
 
         @Override
         public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
@@ -532,6 +533,12 @@
         }
 
         @Override
+        public StatsLatencyLogger withCardinality(int cardinality) {
+            this.mCardinality = cardinality;
+            return this;
+        }
+
+        @Override
         public void log(EventEnum event) {
             if (IS_VERBOSE) {
                 String name = (event instanceof Enum) ? ((Enum) event).name() :
@@ -549,7 +556,8 @@
                     mLatencyInMillis, // latency_in_millis
                     mType.getId(), //type
                     mQueryLength, // query_length
-                    mSubEventType // sub_event_type
+                    mSubEventType, // sub_event_type
+                    mCardinality // cardinality
             );
         }
     }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ff5af28..f17f074 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -213,6 +213,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * A list of recent tasks.
@@ -1320,6 +1321,29 @@
         return null;
     }
 
+    /**
+     * Returns a {@link TaskView} that has taskIds matching {@code taskIds} or null if no match.
+     */
+    @Nullable
+    public TaskView getTaskViewByTaskIds(int[] taskIds) {
+        if (!hasAnyValidTaskIds(taskIds)) {
+            return null;
+        }
+
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = requireTaskViewAt(i);
+            if (Arrays.equals(taskIds, taskView.getTaskIds())) {
+                return taskView;
+            }
+        }
+        return null;
+    }
+
+    /** Returns false if {@code taskIds} is null or contains invalid values, true otherwise */
+    private boolean hasAnyValidTaskIds(int[] taskIds) {
+        return taskIds != null && !Arrays.equals(taskIds, INVALID_TASK_IDS);
+    }
+
     public void setOverviewStateEnabled(boolean enabled) {
         mOverviewStateEnabled = enabled;
         updateTaskStackListenerState();
@@ -1589,10 +1613,10 @@
             return;
         }
 
-        int currentTaskId = INVALID_TASK_ID;
+        int[] currentTaskId = INVALID_TASK_IDS;
         TaskView currentTaskView = getTaskViewAt(mCurrentPage);
         if (currentTaskView != null && currentTaskView.getTask() != null) {
-            currentTaskId = currentTaskView.getTask().key.id;
+            currentTaskId = currentTaskView.getTaskIds();
         }
 
         // Unload existing visible task data
@@ -1604,8 +1628,8 @@
 
         // Save running task ID if it exists before rebinding all taskViews, otherwise the task from
         // the runningTaskView currently bound could get assigned to another TaskView
-        int runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId)[0];
-        int focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId)[0];
+        int[] runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId);
+        int[] focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId);
 
         // Removing views sets the currentPage to 0, so we save this and restore it after
         // the new set of views are added
@@ -1699,7 +1723,7 @@
         }
 
         // Keep same previous focused task
-        TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
+        TaskView newFocusedTaskView = getTaskViewByTaskIds(focusedTaskId);
         // If the list changed, maybe the focused task doesn't exist anymore
         if (newFocusedTaskView == null && getTaskViewCount() > 0) {
             newFocusedTaskView = getTaskViewAt(0);
@@ -1716,10 +1740,10 @@
         updateChildTaskOrientations();
 
         TaskView newRunningTaskView = null;
-        if (runningTaskId != INVALID_TASK_ID) {
+        if (hasAnyValidTaskIds(runningTaskId)) {
             // Update mRunningTaskViewId to be the new TaskView that was assigned by binding
             // the full list of tasks to taskViews
-            newRunningTaskView = getTaskViewByTaskId(runningTaskId);
+            newRunningTaskView = getTaskViewByTaskIds(runningTaskId);
             if (newRunningTaskView != null) {
                 mRunningTaskViewId = newRunningTaskView.getTaskViewId();
             } else {
@@ -1731,15 +1755,15 @@
         if (mNextPage != INVALID_PAGE) {
             // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
             mCurrentPage = previousCurrentPage;
-            if (currentTaskId != INVALID_TASK_ID) {
-                currentTaskView = getTaskViewByTaskId(currentTaskId);
+            if (hasAnyValidTaskIds(currentTaskId)) {
+                currentTaskView = getTaskViewByTaskIds(currentTaskId);
                 if (currentTaskView != null) {
                     targetPage = indexOfChild(currentTaskView);
                 }
             }
         } else {
             // Set the current page to the running task, but not if settling on new task.
-            if (runningTaskId != INVALID_TASK_ID) {
+            if (hasAnyValidTaskIds(runningTaskId)) {
                 targetPage = indexOfChild(newRunningTaskView);
             } else if (getTaskViewCount() > 0) {
                 TaskView taskView = requireTaskViewAt(0);
@@ -2210,8 +2234,8 @@
         // Update the task data for the in/visible children
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView taskView = requireTaskViewAt(i);
-            Task task = taskView.getTask();
-            if (task == null) {
+            TaskIdAttributeContainer[] containers = taskView.getTaskIdAttributeContainers();
+            if (containers[0] == null && containers[1] == null) {
                 continue;
             }
             int index = indexOfChild(taskView);
@@ -2222,34 +2246,43 @@
                 visible = lower <= index && index <= upper;
             }
             if (visible) {
-                boolean skipLoadingTask = false;
+                // Default update all non-null tasks, then remove running ones
+                List<Task> tasksToUpdate = Arrays.stream(containers).filter(Objects::nonNull)
+                        .map(TaskIdAttributeContainer::getTask)
+                        .collect(Collectors.toCollection(ArrayList::new));
                 if (mTmpRunningTasks != null) {
                     for (Task t : mTmpRunningTasks) {
-                        if (task == t) {
-                            // Skip loading if this is the task that we are animating into
-                            skipLoadingTask = true;
-                            break;
-                        }
+                        // Skip loading if this is the task that we are animating into
+                        // TODO(b/280812109) change this equality check to use A.equals(B)
+                        tasksToUpdate.removeIf(task -> task == t);
                     }
                 }
-                if (skipLoadingTask) {
+                if (tasksToUpdate.isEmpty()) {
                     continue;
                 }
-                if (!mHasVisibleTaskData.get(task.key.id)) {
-                    // Ignore thumbnail update if it's current running task during the gesture
-                    // We snapshot at end of gesture, it will update then
-                    int changes = dataChanges;
-                    if (taskView == getRunningTaskView() && mGestureActive) {
-                        changes &= ~TaskView.FLAG_UPDATE_THUMBNAIL;
+                for (Task task : tasksToUpdate) {
+                    if (!mHasVisibleTaskData.get(task.key.id)) {
+                        // Ignore thumbnail update if it's current running task during the gesture
+                        // We snapshot at end of gesture, it will update then
+                        int changes = dataChanges;
+                        if (taskView == getRunningTaskView() && mGestureActive) {
+                            changes &= ~TaskView.FLAG_UPDATE_THUMBNAIL;
+                        }
+                        taskView.onTaskListVisibilityChanged(true /* visible */, changes);
                     }
-                    taskView.onTaskListVisibilityChanged(true /* visible */, changes);
+                    mHasVisibleTaskData.put(task.key.id, visible);
                 }
-                mHasVisibleTaskData.put(task.key.id, visible);
             } else {
-                if (mHasVisibleTaskData.get(task.key.id)) {
-                    taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
+                for (TaskIdAttributeContainer container : containers) {
+                    if (container == null) {
+                        continue;
+                    }
+
+                    if (mHasVisibleTaskData.get(container.getTask().key.id)) {
+                        taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
+                    }
+                    mHasVisibleTaskData.delete(container.getTask().key.id);
                 }
-                mHasVisibleTaskData.delete(task.key.id);
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 796cd62..134ef6c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -92,6 +92,7 @@
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
+import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.TaskIconCache;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskThumbnailCache;
@@ -652,7 +653,7 @@
      *         index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
      */
     public int[] getTaskIds() {
-        return mTaskIdContainer;
+        return Arrays.copyOf(mTaskIdContainer, mTaskIdContainer.length);
     }
 
     public boolean containsMultipleTasks() {
@@ -802,6 +803,14 @@
                     recentsView.addSideTaskLaunchCallback(callbackList);
                     return callbackList;
                 }
+                if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+                    // If the recents transition is running (ie. in live tile mode), then the start
+                    // of a new task will merge into the existing transition and it currently will
+                    // not be run independently, so we need to rely on the onTaskAppeared() call
+                    // for the new task to trigger the side launch callback to flush this runnable
+                    // list (which is usually flushed when the app launch animation finishes)
+                    recentsView.addSideTaskLaunchCallback(opts.onEndCallback);
+                }
                 return opts.onEndCallback;
             } else {
                 notifyTaskLaunchFailed(TAG);
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
new file mode 100644
index 0000000..9c0b2bf
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/AbstractTaplTestsTaskbar.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.Taskbar;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.TestUtil;
+
+import org.junit.After;
+import org.junit.Assume;
+
+import java.util.List;
+
+public class AbstractTaplTestsTaskbar extends AbstractQuickStepTest {
+
+    protected static final String TEST_APP_NAME = "LauncherTestApp";
+    protected static final String TEST_APP_PACKAGE =
+            getInstrumentation().getContext().getPackageName();
+    protected static final String CALCULATOR_APP_PACKAGE =
+            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+    protected AutoCloseable mLauncherLayout;
+    protected boolean mTaskbarWasInTransientMode;
+
+
+    @Override
+    public void setUp() throws Exception {
+        Assume.assumeTrue(mLauncher.isTablet());
+        super.setUp();
+
+        LauncherLayoutBuilder layoutBuilder = new LauncherLayoutBuilder().atHotseat(0).putApp(
+                "com.google.android.apps.nexuslauncher.tests",
+                "com.android.launcher3.testcomponent.BaseTestingActivity");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
+        TaplTestsLauncher3.initialize(this);
+
+        startAppFast(CALCULATOR_APP_PACKAGE);
+        mLauncher.enableBlockTimeout(true);
+        mLauncher.showTaskbarIfHidden();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        setTaskbarMode(mLauncher, mTaskbarWasInTransientMode);
+        mLauncher.enableBlockTimeout(false);
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
+    }
+
+    protected static boolean isTaskbarInTransientMode(Context context) {
+        return DisplayController.isTransientTaskbar(context);
+    }
+
+    protected Taskbar getTaskbar() {
+        Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
+        List<String> taskbarIconNames = taskbar.getIconNames();
+        List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
+
+        assertEquals("Taskbar and hotseat icon counts do not match",
+                taskbarIconNames.size(), hotseatIconNames.size());
+
+        for (int i = 0; i < taskbarIconNames.size(); i++) {
+            assertEquals("Taskbar and Hotseat icons do not match",
+                    taskbarIconNames, hotseatIconNames);
+        }
+
+        return taskbar;
+    }
+
+    protected static void setTaskbarMode(LauncherInstrumentation launcher,
+            boolean expectTransientTaskbar) {
+        launcher.enableTransientTaskbar(expectTransientTaskbar);
+        launcher.recreateTaskbar();
+        launcher.checkForAnomaly(true, true);
+        AbstractLauncherUiTest.checkDetectedLeaks(launcher);
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
new file mode 100644
index 0000000..ee0eeb2
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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;
+
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsPersistentTaskbar extends AbstractTaplTestsTaskbar {
+
+    @Override
+    public void setUp() throws Exception {
+        mTaskbarWasInTransientMode = isTaskbarInTransientMode(mTargetContext);
+        setTaskbarMode(mLauncher, false);
+        super.setUp();
+    }
+
+    @Test
+    @TaskbarModeSwitch(mode = PERSISTENT)
+    public void testHideShowTaskbar() {
+        getTaskbar().hide();
+        mLauncher.getLaunchedAppState().showTaskbar();
+    }
+
+    @Test
+    @TaskbarModeSwitch(mode = PERSISTENT)
+    public void testHideTaskbarPersistsOnRecreate() {
+        getTaskbar().hide();
+        mLauncher.recreateTaskbar();
+        mLauncher.getLaunchedAppState().assertTaskbarHidden();
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index f5c78f6..021e118 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -15,121 +15,68 @@
  */
 package com.android.quickstep;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
-import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
-import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
-
-import static junit.framework.TestCase.assertEquals;
-
-import android.content.Intent;
+import static com.android.quickstep.TaplTestsTaskbar.TaskbarMode.PERSISTENT;
+import static com.android.quickstep.TaplTestsTaskbar.TaskbarMode.TRANSIENT;
 
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.tapl.Taskbar;
-import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
-import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
-import org.junit.After;
-import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-import java.util.List;
+import java.util.Arrays;
+import java.util.Collection;
 
 @LargeTest
-@RunWith(AndroidJUnit4.class)
-public class TaplTestsTaskbar extends AbstractQuickStepTest {
+@RunWith(Parameterized.class)
+public class TaplTestsTaskbar extends AbstractTaplTestsTaskbar {
 
-    private static final String TEST_APP_NAME = "LauncherTestApp";
-    private static final String TEST_APP_PACKAGE =
-            getInstrumentation().getContext().getPackageName();
-    private static final String CALCULATOR_APP_PACKAGE =
-            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+    private final TaplTestsTaskbar.TaskbarMode mTaskbarMode;
 
-    private AutoCloseable mLauncherLayout;
+    public enum TaskbarMode {
+        TRANSIENT, PERSISTENT
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {PERSISTENT}, {TRANSIENT}
+        });
+    }
+
+    public TaplTestsTaskbar(TaskbarMode mode) {
+        mTaskbarMode = mode;
+    }
 
     @Override
     public void setUp() throws Exception {
-        Assume.assumeTrue(mLauncher.isTablet());
+        mTaskbarWasInTransientMode = isTaskbarInTransientMode(mTargetContext);
+        setTaskbarMode(mLauncher, isTaskbarTestModeTransient());
         super.setUp();
-
-        LauncherLayoutBuilder layoutBuilder = new LauncherLayoutBuilder().atHotseat(0).putApp(
-                "com.google.android.apps.nexuslauncher.tests",
-                "com.android.launcher3.testcomponent.BaseTestingActivity");
-        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
-        TaplTestsLauncher3.initialize(this);
-
-        startAppFast(CALCULATOR_APP_PACKAGE);
-        mLauncher.enableBlockTimeout(true);
-        mLauncher.showTaskbarIfHidden();
     }
 
-    @After
-    public void tearDown() throws Exception {
-        mLauncher.enableBlockTimeout(false);
-        if (mLauncherLayout != null) {
-            mLauncherLayout.close();
+    @Test
+    public void testLaunchApp() {
+        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+        // We are using parameterized test runner to share code between different test cases with
+        // taskbar variants. But, sometimes we only need to assert things for particular Taskbar
+        // variants.
+        if (isTaskbarTestModeTransient() && mLauncher.getNavigationModel()
+                != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
+            mLauncher.getLaunchedAppState().assertTaskbarHidden();
         }
     }
 
     @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testHideShowTaskbar() {
-        getTaskbar().hide();
-        mLauncher.getLaunchedAppState().showTaskbar();
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testHideTaskbarPersistsOnRecreate() {
-        getTaskbar().hide();
-        mLauncher.recreateTaskbar();
-        mLauncher.getLaunchedAppState().assertTaskbarHidden();
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchApp() throws Exception {
-        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchApp() throws Exception {
-        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-        mLauncher.getLaunchedAppState().assertTaskbarHidden();
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testOpenMenu() throws Exception {
+    public void testOpenMenu() {
         getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
     }
 
     @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientOpenMenu() throws Exception {
-        getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchShortcut() throws Exception {
-        getTaskbar().getAppIcon(TEST_APP_NAME)
-                .openDeepShortcutMenu()
-                .getMenuItem("Shortcut 1")
-                .launch(TEST_APP_PACKAGE);
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchShortcut() throws Exception {
+    public void testLaunchShortcut() {
         getTaskbar().getAppIcon(TEST_APP_NAME)
                 .openDeepShortcutMenu()
                 .getMenuItem("Shortcut 1")
@@ -139,27 +86,21 @@
     @Test
     @ScreenRecord // b/231615831
     @PortraitLandscape
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchAppInSplitscreen() throws Exception {
+    public void testLaunchAppInSplitscreen() {
         getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
                 TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+        // We are using parameterized test runner to share code between different test cases with
+        // taskbar variants. But, sometimes we only need to assert things for particular Taskbar
+        // variants.
+        if (isTaskbarTestModeTransient()) {
+            mLauncher.getLaunchedAppState().assertTaskbarHidden();
+        }
     }
 
     @Test
     @ScreenRecord // b/231615831
     @PortraitLandscape
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchAppInSplitscreen() throws Exception {
-        getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
-                TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
-        mLauncher.getLaunchedAppState().assertTaskbarHidden();
-    }
-
-    @Test
-    @ScreenRecord // b/231615831
-    @PortraitLandscape
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchShortcutInSplitscreen() throws Exception {
+    public void testLaunchShortcutInSplitscreen() {
         getTaskbar().getAppIcon(TEST_APP_NAME)
                 .openDeepShortcutMenu()
                 .getMenuItem("Shortcut 1")
@@ -167,53 +108,17 @@
     }
 
     @Test
-    @ScreenRecord // b/231615831
-    @PortraitLandscape
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchShortcutInSplitscreen() throws Exception {
-        getTaskbar().getAppIcon(TEST_APP_NAME)
-                .openDeepShortcutMenu()
-                .getMenuItem("Shortcut 1")
-                .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchApp_FromTaskbarAllApps() throws Exception {
+    public void testLaunchApp_fromTaskbarAllApps() {
         getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
     }
 
     @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchApp_FromTaskbarAllApps() throws Exception {
-        getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testOpenMenu_FromTaskbarAllApps() throws Exception {
+    public void testOpenMenu_fromTaskbarAllApps() {
         getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
     }
 
     @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientOpenMenu_FromTaskbarAllApps() throws Exception {
-        getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchShortcut_FromTaskbarAllApps() throws Exception {
-        getTaskbar().openAllApps()
-                .getAppIcon(TEST_APP_NAME)
-                .openDeepShortcutMenu()
-                .getMenuItem("Shortcut 1")
-                .launch(TEST_APP_PACKAGE);
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchShortcut_FromTaskbarAllApps() throws Exception {
+    public void testLaunchShortcut_fromTaskbarAllApps() {
         getTaskbar().openAllApps()
                 .getAppIcon(TEST_APP_NAME)
                 .openDeepShortcutMenu()
@@ -224,8 +129,7 @@
     @Test
     @ScreenRecord // b/231615831
     @PortraitLandscape
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
+    public void testLaunchAppInSplitscreen_fromTaskbarAllApps() {
         getTaskbar().openAllApps()
                 .getAppIcon(TEST_APP_NAME)
                 .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
@@ -234,18 +138,7 @@
     @Test
     @ScreenRecord // b/231615831
     @PortraitLandscape
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
-        getTaskbar().openAllApps()
-                .getAppIcon(TEST_APP_NAME)
-                .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
-    }
-
-    @Test
-    @ScreenRecord // b/231615831
-    @PortraitLandscape
-    @TaskbarModeSwitch(mode = PERSISTENT)
-    public void testLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
+    public void testLaunchShortcutInSplitscreen_fromTaskbarAllApps() {
         getTaskbar().openAllApps()
                 .getAppIcon(TEST_APP_NAME)
                 .openDeepShortcutMenu()
@@ -253,64 +146,7 @@
                 .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
     }
 
-    @Test
-    @ScreenRecord // b/231615831
-    @PortraitLandscape
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testTransientLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
-        getTaskbar().openAllApps()
-                .getAppIcon(TEST_APP_NAME)
-                .openDeepShortcutMenu()
-                .getMenuItem("Shortcut 1")
-                .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testShowTaskbarUnstashHintOnHover() {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testUnstashTaskbarOnScreenBottomEdgeHover() {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Test
-    @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testHoverBelowHintedTaskbarToUnstash() {
-        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
-            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
-            mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private Taskbar getTaskbar() {
-        Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
-        List<String> taskbarIconNames = taskbar.getIconNames();
-        List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
-
-        assertEquals("Taskbar and hotseat icon counts do not match",
-                taskbarIconNames.size(), hotseatIconNames.size());
-
-        for (int i = 0; i < taskbarIconNames.size(); i++) {
-            assertEquals("Taskbar and Hotseat icons do not match",
-                    taskbarIconNames, hotseatIconNames);
-        }
-
-        return taskbar;
+    private boolean isTaskbarTestModeTransient() {
+        return TRANSIENT == mTaskbarMode;
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
new file mode 100644
index 0000000..6b61f18
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
+
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.util.TestUtil;
+import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsTransientTaskbar extends AbstractTaplTestsTaskbar {
+
+    @Override
+    public void setUp() throws Exception {
+        mTaskbarWasInTransientMode = isTaskbarInTransientMode(mTargetContext);
+        setTaskbarMode(mLauncher, true);
+        super.setUp();
+    }
+
+    @Test
+    @TaskbarModeSwitch(mode = TRANSIENT)
+    public void testShowTaskbarUnstashHintOnHover() {
+        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
+            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+            mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    @TaskbarModeSwitch(mode = TRANSIENT)
+    public void testUnstashTaskbarOnScreenBottomEdgeHover() {
+        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
+            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+            mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    @TaskbarModeSwitch(mode = TRANSIENT)
+    public void testHoverBelowHintedTaskbarToUnstash() {
+        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
+            getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+            mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index acfd54c..65542cf 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -33,14 +33,10 @@
 import com.android.launcher3.statemanager.StateManager
 import com.android.launcher3.util.ComponentKey
 import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
-import com.android.launcher3.util.mock
 import com.android.launcher3.util.withArgCaptor
 import com.android.quickstep.RecentsModel
 import com.android.quickstep.SystemUiProxy
 import com.android.systemui.shared.recents.model.Task
-import java.util.ArrayList
-import java.util.function.Consumer
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNull
@@ -50,7 +46,9 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
 
 @RunWith(AndroidJUnit4::class)
 class SplitSelectStateControllerTest {
@@ -355,6 +353,7 @@
     @Test
     fun secondPendingIntentSet() {
         val itemInfo = ItemInfo()
+        `when`(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle)
         splitSelectStateController.setInitialTaskSelect(null, 0, itemInfo, null, 1)
         splitSelectStateController.setSecondTask(pendingIntent)
         assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index ce8d901..8136534 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -199,6 +199,9 @@
         <attr name="demoModeLayoutId" format="reference" />
         <attr name="isScalable" format="boolean" />
         <attr name="devicePaddingId" format="reference" />
+        <!-- File that contains the specs for the workspace.
+        Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
+        <attr name="workspaceSpecsId" format="reference" />
         <!-- By default all categories are enabled -->
         <attr name="deviceCategory" format="integer">
             <!-- Enable on phone only -->
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 58a447d..9e73453 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -75,7 +75,6 @@
     <color name="text_color_tertiary_dark">#CCFFFFFF</color>
 
     <color name="wallpaper_popup_scrim">?android:attr/colorAccent</color>
-    <color name="wallpaper_scrim_color">#0D878787</color>
 
     <color name="workspace_accent_color_light">#ff8df5e3</color>
     <color name="workspace_accent_color_dark">#ff3d665f</color>
diff --git a/res/xml/dynamic_resources.xml b/res/xml/dynamic_resources.xml
index 3a3e239..f7a631b 100644
--- a/res/xml/dynamic_resources.xml
+++ b/res/xml/dynamic_resources.xml
@@ -4,6 +4,5 @@
     <entry id="@color/delete_target_hover_tint" />
     <entry id="@color/delete_target_hover_tint" />
     <entry id="@color/delete_target_hover_tint" />
-    <entry id="@color/wallpaper_scrim_color" />
 
 </DynamicResources>
diff --git a/res/xml/widget_sections.xml b/res/xml/widget_sections.xml
index 6165bf7..742991f 100644
--- a/res/xml/widget_sections.xml
+++ b/res/xml/widget_sections.xml
@@ -26,6 +26,6 @@
         launcher:category="1"
         launcher:sectionDrawable="@drawable/ic_note_taking_widget_category"
         launcher:sectionTitle="@string/widget_category_note_taking">
-        <widget launcher:provider="com.android.settings/com.android.settings.notetask.shortcut.CreateNoteTaskShortcutActivity" />
+        <widget launcher:provider="com.android.systemui/.notetask.shortcut.CreateNoteTaskShortcutActivity" />
     </section>
 </widget-sections>
\ No newline at end of file
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8675226..0231090 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -101,6 +101,7 @@
     public final float aspectRatio;
 
     public final boolean isScalableGrid;
+    public final boolean isResponsiveGrid;
     private final int mTypeIndex;
 
     /**
@@ -293,6 +294,10 @@
         this.rotationHint = windowBounds.rotationHint;
         mInsets.set(windowBounds.insets);
 
+        // TODO(b/241386436):
+        //  for testing that the flag works only, shouldn't change any launcher behaviour
+        isResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE;
+
         isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
         // Determine device posture.
         mInfo = info;
@@ -1577,6 +1582,7 @@
 
         writer.println(prefix + "\taspectRatio:" + aspectRatio);
 
+        writer.println(prefix + "\tisResponsiveGrid:" + isResponsiveGrid);
         writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
 
         writer.println(prefix + "\tinv.numRows: " + inv.numRows);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 3aa582d..376f54d 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -48,6 +48,7 @@
 import androidx.annotation.XmlRes;
 import androidx.core.content.res.ResourcesCompat;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.provider.RestoreDbTask;
@@ -105,7 +106,7 @@
     static final int INDEX_TWO_PANEL_PORTRAIT = 2;
     static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
 
-    /** These resources are used to override the device profile  */
+    /** These resources are used to override the device profile */
     private static final String RES_GRID_NUM_ROWS = "grid_num_rows";
     private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
     private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
@@ -177,6 +178,8 @@
     protected boolean isScalable;
     @XmlRes
     public int devicePaddingId = INVALID_RESOURCE_HANDLE;
+    @XmlRes
+    public int workspaceSpecsId = INVALID_RESOURCE_HANDLE;
 
     public String dbFile;
     public int defaultLayoutId;
@@ -350,6 +353,7 @@
 
         isScalable = closestProfile.isScalable;
         devicePaddingId = closestProfile.devicePaddingId;
+        workspaceSpecsId = closestProfile.mWorkspaceSpecsId;
         this.deviceType = deviceType;
 
         inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
@@ -795,6 +799,7 @@
 
         private final boolean isScalable;
         private final int devicePaddingId;
+        private final int mWorkspaceSpecsId;
 
         public GridOption(Context context, AttributeSet attrs) {
             TypedArray a = context.obtainStyledAttributes(
@@ -836,7 +841,7 @@
 
             inlineNavButtonsEndSpacing =
                     a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing,
-                    R.dimen.taskbar_button_margin_default);
+                            R.dimen.taskbar_button_margin_default);
 
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
@@ -856,6 +861,13 @@
             deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory,
                     DEVICE_CATEGORY_ALL);
 
+            if (FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE.get()) {
+                mWorkspaceSpecsId = a.getResourceId(
+                        R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE);
+            } else {
+                mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE;
+            }
+
             int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
                     DONT_INLINE_QSB);
             inlineQsb[INDEX_DEFAULT] =
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 29b0f08..ffd56cc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -214,6 +214,7 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ComposeInitializer;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.views.FloatingSurfaceView;
 import com.android.launcher3.views.OptionsPopupView;
@@ -553,6 +554,8 @@
         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
 
         setContentView(getRootView());
+        ComposeInitializer.initCompose(this);
+
         if (mOnInitialBindListener != null) {
             getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
         }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 7f04860..8dc1204 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -372,10 +372,6 @@
             "ENABLE_ENFORCED_ROUNDED_CORNERS", ENABLED,
             "Enforce rounded corners on all App Widgets");
 
-    public static final BooleanFlag ENABLE_WALLPAPER_SCRIM = getDebugFlag(270393604,
-            "ENABLE_WALLPAPER_SCRIM", DISABLED,
-            "Enables scrim over wallpaper for text protection.");
-
     public static final BooleanFlag ENABLE_ICON_LABEL_AUTO_SCALING = getDebugFlag(270393294,
             "ENABLE_ICON_LABEL_AUTO_SCALING", ENABLED,
             "Enables scaling/spacing for icon labels to make more characters visible");
@@ -415,11 +411,15 @@
 
     // TODO(Block 31)
     public static final BooleanFlag ENABLE_SPLIT_LAUNCH_DATA_REFACTOR = getDebugFlag(279494325,
-            "ENABLE_SPLIT_LAUNCH_DATA_REFACTOR", DISABLED,
+            "ENABLE_SPLIT_LAUNCH_DATA_REFACTOR", ENABLED,
             "Use refactored split launching code path");
 
     // TODO(Block 32): Empty block
 
+    public static final BooleanFlag ENABLE_RESPONSIVE_WORKSPACE = getDebugFlag(241386436,
+            "ENABLE_RESPONSIVE_WORKSPACE", DISABLED,
+            "Enables new workspace grid calculations method.");
+
     public static class BooleanFlag {
 
         private final boolean mCurrentValue;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 48239ae..2c1100f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -74,7 +74,6 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemFactory;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.Thunk;
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index be995bc..21ebc98 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -36,13 +36,10 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.testing.shared.ResourceUtils;
-import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.ScreenOnTracker;
 import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
 import com.android.launcher3.util.Themes;
-import com.android.systemui.plugins.ResourceProvider;
 
 /**
  * View scrim which draws behind hotseat and workspace
@@ -100,10 +97,8 @@
     private static final int ALPHA_MASK_BITMAP_DP = 200;
     private static final int ALPHA_MASK_WIDTH_DP = 2;
 
-    private boolean mDrawTopScrim, mDrawBottomScrim, mDrawWallpaperScrim;
+    private boolean mDrawTopScrim, mDrawBottomScrim;
 
-    private final RectF mWallpaperScrimRect = new RectF();
-    private final Paint mWallpaperScrimPaint = new Paint();
     private final RectF mFinalMaskRect = new RectF();
     private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
     private final Bitmap mBottomMask;
@@ -118,7 +113,6 @@
 
     private boolean mAnimateScrimOnNextDraw = false;
     private float mSysUiAnimMultiplier = 1;
-    private int mWallpaperScrimMaxAlpha;
 
     public SysUiScrim(View view) {
         mRoot = view;
@@ -135,14 +129,6 @@
             mHideSysUiScrim = true;
         }
 
-        mDrawWallpaperScrim = FeatureFlags.ENABLE_WALLPAPER_SCRIM.get()
-                && !Themes.getAttrBoolean(view.getContext(), R.attr.isMainColorDark)
-                && !Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText);
-        ResourceProvider rp = DynamicResource.provider(view.getContext());
-        int wallpaperScrimColor = rp.getColor(R.color.wallpaper_scrim_color);
-        mWallpaperScrimMaxAlpha = Color.alpha(wallpaperScrimColor);
-        mWallpaperScrimPaint.setColor(wallpaperScrimColor);
-
         view.addOnAttachStateChangeListener(this);
     }
 
@@ -167,9 +153,6 @@
                 mAnimateScrimOnNextDraw = false;
             }
 
-            if (mDrawWallpaperScrim) {
-                canvas.drawRect(mWallpaperScrimRect, mWallpaperScrimPaint);
-            }
             if (mDrawTopScrim) {
                 mTopScrim.draw(canvas);
             }
@@ -228,7 +211,6 @@
             mTopScrim.setBounds(0, 0, w, h);
             mFinalMaskRect.set(0, h - mMaskHeight, w, h);
         }
-        mWallpaperScrimRect.set(0, 0, w, h);
     }
 
     private void setSysUiProgress(float progress) {
@@ -251,7 +233,6 @@
         if (mTopScrim != null) {
             mTopScrim.setAlpha(Math.round(255 * factor));
         }
-        mWallpaperScrimPaint.setAlpha(Math.round(mWallpaperScrimMaxAlpha * factor));
     }
 
     private Bitmap createDitheredAlphaMask() {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 8197b73..15f3538 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -891,6 +891,12 @@
             return this;
         }
 
+
+        /** Sets cardinality of the event. */
+        default StatsLatencyLogger withCardinality(int cardinality) {
+            return this;
+        }
+
         /**
          * Sets packageId of log message.
          */
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index bc492fd..8274789 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -241,8 +241,8 @@
                     deepShortcutCount,
                     popupDataProvider.getNotificationKeysForItem(item),
                     systemShortcuts);
-            launcher.tryClearAccessibilityFocus(icon);
         }
+        launcher.tryClearAccessibilityFocus(icon);
         launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
         container.requestFocus();
         return container;
diff --git a/src/com/android/launcher3/views/ComposeInitializer.java b/src/com/android/launcher3/views/ComposeInitializer.java
new file mode 100644
index 0000000..0929885
--- /dev/null
+++ b/src/com/android/launcher3/views/ComposeInitializer.java
@@ -0,0 +1,229 @@
+/*
+ * 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.views;
+
+import android.os.Build;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.ViewTreeLifecycleOwner;
+import androidx.savedstate.SavedStateRegistry;
+import androidx.savedstate.SavedStateRegistryController;
+import androidx.savedstate.SavedStateRegistryOwner;
+import androidx.savedstate.ViewTreeSavedStateRegistryOwner;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * An initializer to use Compose for classes implementing {@code ActivityContext}. This allows
+ * adding ComposeView to ViewTree outside a {@link androidx.activity.ComponentActivity}.
+ */
+public final class ComposeInitializer {
+    /**
+     * Performs the initialization to use Compose in the ViewTree of {@code target}.
+     */
+    public static void initCompose(ActivityContext target) {
+        getContentChild(target).addOnAttachStateChangeListener(
+                new View.OnAttachStateChangeListener() {
+
+                    @Override
+                    public void onViewAttachedToWindow(View v) {
+                        ComposeInitializer.onAttachedToWindow(v);
+                    }
+
+                    @Override
+                    public void onViewDetachedFromWindow(View v) {
+                        ComposeInitializer.onDetachedFromWindow(v);
+                    }
+                });
+    }
+
+    /**
+     * Find the "content child" for {@code target}.
+     *
+     * @see "WindowRecomposer.android.kt: [View.contentChild]"
+     */
+    private static View getContentChild(ActivityContext target) {
+        View self = target.getDragLayer();
+        ViewParent parent = self.getParent();
+        while (parent instanceof View parentView) {
+            if (parentView.getId() == android.R.id.content) return self;
+            self = parentView;
+            parent = self.getParent();
+        }
+        return self;
+    }
+
+    /**
+     * Function to be called on your window root view's [View.onAttachedToWindow] function.
+     */
+    private static void onAttachedToWindow(View root) {
+        if (ViewTreeLifecycleOwner.get(root) != null) {
+            throw new IllegalStateException(
+                    "View " + root + " already has a LifecycleOwner");
+        }
+
+        ViewParent parent = root.getParent();
+        if (parent instanceof View && ((View) parent).getId() != android.R.id.content) {
+            throw new IllegalStateException(
+                    "ComposeInitializer.onContentChildAttachedToWindow(View) must be called on "
+                            + "the content child. Outside of activities and dialogs, this is "
+                            + "usually the top-most View of a window.");
+        }
+
+        // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root]
+        // is both visible and focused.
+        ViewLifecycleOwner lifecycleOwner = new ViewLifecycleOwner(root);
+
+        // We must call [ViewLifecycleOwner.onCreate] after creating the
+        // [SavedStateRegistryOwner] because `onCreate` might move the lifecycle state to STARTED
+        // which will make [SavedStateRegistryController.performRestore] throw.
+        lifecycleOwner.onCreate();
+
+        // Set the owners on the root. They will be reused by any ComposeView inside the root
+        // hierarchy.
+        ViewTreeLifecycleOwner.set(root, lifecycleOwner);
+        ViewTreeSavedStateRegistryOwner.set(root, lifecycleOwner);
+    }
+
+    /**
+     * Function to be called on your window root view's [View.onDetachedFromWindow] function.
+     */
+    private static void onDetachedFromWindow(View root) {
+        final LifecycleOwner lifecycleOwner = ViewTreeLifecycleOwner.get(root);
+        if (lifecycleOwner != null) {
+            ((ViewLifecycleOwner) lifecycleOwner).onDestroy();
+        }
+        ViewTreeLifecycleOwner.set(root, null);
+        ViewTreeSavedStateRegistryOwner.set(root, null);
+    }
+
+    /**
+     * A [LifecycleOwner] for a [View] that updates lifecycle state based on window state.
+     *
+     * Also a trivial implementation of [SavedStateRegistryOwner] that does not do any save or
+     * restore. This works for processes similar to the SystemUI process, which is always running
+     * and top-level windows using this initialization are created once, when the process is
+     * started.
+     *
+     * The implementation requires the caller to call [onCreate] and [onDestroy] when the view is
+     * attached to or detached from a view hierarchy. After [onCreate] and before [onDestroy] is
+     * called, the implementation monitors window state in the following way
+     * * If the window is not visible, we are in the [Lifecycle.State.CREATED] state
+     * * If the window is visible but not focused, we are in the [Lifecycle.State.STARTED] state
+     * * If the window is visible and focused, we are in the [Lifecycle.State.RESUMED] state
+     *
+     * Or in table format:
+     * ```
+     * ┌───────────────┬───────────────────┬──────────────┬─────────────────┐
+     * │ View attached │ Window Visibility │ Window Focus │ Lifecycle State │
+     * ├───────────────┼───────────────────┴──────────────┼─────────────────┤
+     * │ Not attached  │                 Any              │       N/A       │
+     * ├───────────────┼───────────────────┬──────────────┼─────────────────┤
+     * │               │    Not visible    │     Any      │     CREATED     │
+     * │               ├───────────────────┼──────────────┼─────────────────┤
+     * │   Attached    │                   │   No focus   │     STARTED     │
+     * │               │      Visible      ├──────────────┼─────────────────┤
+     * │               │                   │  Has focus   │     RESUMED     │
+     * └───────────────┴───────────────────┴──────────────┴─────────────────┘
+     * ```
+     */
+    private static class ViewLifecycleOwner implements SavedStateRegistryOwner {
+        private final ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusListener =
+                hasFocus -> updateState();
+        private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+
+        private final SavedStateRegistryController mSavedStateRegistryController =
+                SavedStateRegistryController.create(this);
+
+        private final View mView;
+        private final Api34Impl mApi34Impl;
+
+        ViewLifecycleOwner(View view) {
+            mView = view;
+            if (Utilities.ATLEAST_U) {
+                mApi34Impl = new Api34Impl();
+            } else {
+                mApi34Impl = null;
+            }
+
+            mSavedStateRegistryController.performRestore(null);
+        }
+
+        @NonNull
+        @Override
+        public Lifecycle getLifecycle() {
+            return mLifecycleRegistry;
+        }
+
+        @NonNull
+        @Override
+        public SavedStateRegistry getSavedStateRegistry() {
+            return mSavedStateRegistryController.getSavedStateRegistry();
+        }
+
+        void onCreate() {
+            mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
+            if (Utilities.ATLEAST_U) {
+                mApi34Impl.addOnWindowVisibilityChangeListener();
+            }
+            mView.getViewTreeObserver().addOnWindowFocusChangeListener(
+                    mWindowFocusListener);
+            updateState();
+        }
+
+        void onDestroy() {
+            if (Utilities.ATLEAST_U) {
+                mApi34Impl.removeOnWindowVisibilityChangeListener();
+            }
+            mView.getViewTreeObserver().removeOnWindowFocusChangeListener(
+                    mWindowFocusListener);
+            mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
+        }
+
+        private void updateState() {
+            Lifecycle.State state =
+                    mView.getWindowVisibility() != View.VISIBLE ? Lifecycle.State.CREATED
+                            : (!mView.hasWindowFocus() ? Lifecycle.State.STARTED
+                                    : Lifecycle.State.RESUMED);
+            mLifecycleRegistry.setCurrentState(state);
+        }
+
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        private class Api34Impl {
+            private final ViewTreeObserver.OnWindowVisibilityChangeListener
+                    mWindowVisibilityListener =
+                    visibility -> updateState();
+
+            void addOnWindowVisibilityChangeListener() {
+                mView.getViewTreeObserver().addOnWindowVisibilityChangeListener(
+                        mWindowVisibilityListener);
+            }
+
+            void removeOnWindowVisibilityChangeListener() {
+                mView.getViewTreeObserver().removeOnWindowVisibilityChangeListener(
+                        mWindowVisibilityListener);
+            }
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 0fe8bee..01f494b 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -50,7 +50,7 @@
     private lateinit var originalWindowManagerProxy: WindowManagerProxy
 
     @Before
-    fun setUp() {
+    open fun setUp() {
         val appContext: Context = ApplicationProvider.getApplicationContext()
         originalWindowManagerProxy = WindowManagerProxy.INSTANCE.get(appContext)
         originalDisplayController = DisplayController.INSTANCE.get(appContext)
@@ -59,7 +59,7 @@
     }
 
     @After
-    fun tearDown() {
+    open fun tearDown() {
         WindowManagerProxy.INSTANCE.initializeForTesting(originalWindowManagerProxy)
         DisplayController.INSTANCE.initializeForTesting(originalDisplayController)
     }
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index 13db6c7..a81413e 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -58,6 +58,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 126.0px (48.0dp)\n" +
                     "\taspectRatio:2.2222223\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 5\n" +
@@ -193,6 +194,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 63.0px (24.0dp)\n" +
                     "\taspectRatio:2.2222223\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 5\n" +
@@ -328,6 +330,7 @@
                     "\tmInsets.right: 126.0px (48.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:2.2222223\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 5\n" +
@@ -463,6 +466,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 63.0px (24.0dp)\n" +
                     "\taspectRatio:2.2222223\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 5\n" +
@@ -599,6 +603,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.6\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:true\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 6\n" +
@@ -735,6 +740,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.6\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:true\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 6\n" +
@@ -871,6 +877,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.6\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:true\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 6\n" +
@@ -1007,6 +1014,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.6\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:true\n" +
                     "\tinv.numRows: 5\n" +
                     "\tinv.numColumns: 6\n" +
@@ -1148,6 +1156,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.2\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 4\n" +
                     "\tinv.numColumns: 4\n" +
@@ -1288,6 +1297,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.2\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 4\n" +
                     "\tinv.numColumns: 4\n" +
@@ -1428,6 +1438,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.2\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 4\n" +
                     "\tinv.numColumns: 4\n" +
@@ -1564,6 +1575,7 @@
                     "\tmInsets.right: 0.0px (0.0dp)\n" +
                     "\tmInsets.bottom: 0.0px (0.0dp)\n" +
                     "\taspectRatio:1.2\n" +
+                    "\tisResponsiveGrid:false\n" +
                     "\tisScalableGrid:false\n" +
                     "\tinv.numRows: 4\n" +
                     "\tinv.numColumns: 4\n" +
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index f52b82d..a59eff7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -223,6 +223,9 @@
                     new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null);
 
             mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
+
+            mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT,
+                    new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null);
         }
     }