Merge "Guard TrackpadStatusBarInputConsumer with canStartSystemGesture" into udc-qpr-dev
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 3819dd4..ecb8365 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -25,6 +25,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.system.Os;
+import android.util.Log;
 
 import androidx.annotation.Keep;
 import androidx.annotation.Nullable;
@@ -61,6 +62,7 @@
                 public void onActivityCreated(Activity activity, Bundle bundle) {
                     sActivities.put(activity, true);
                     ++sActivitiesCreatedCount;
+                    Log.d(TestProtocol.FLAKY_ACTIVITY_COUNT, "onActivityCreated", new Exception());
                 }
 
                 @Override
diff --git a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml b/quickstep/res/layout-sw600dp/allset_navigation.xml
similarity index 71%
copy from quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
copy to quickstep/res/layout-sw600dp/allset_navigation.xml
index 44b3ecb..a47eef4 100644
--- a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
+++ b/quickstep/res/layout-sw600dp/allset_navigation.xml
@@ -25,19 +25,9 @@
         android:background="?android:attr/selectableItemBackground"
         android:minHeight="48dp"
         android:text="@string/allset_navigation_settings"
-        app:layout_constraintTop_toBottomOf="@id/subtitle"
-        app:layout_constraintStart_toStartOf="parent" />
+        android:gravity="center_horizontal"
 
-    <TextView
-        android:id="@+id/hint"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="24dp"
-        android:text="@string/allset_hint"
-        android:textSize="@dimen/allset_page_swipe_up_text_size"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/subtitle"
         app:layout_constraintStart_toStartOf="parent" />
 
 </merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 7ea92b5..2c312a7 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -14,33 +14,43 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
+<androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:id="@+id/root_view"
-    android:background="@color/all_set_page_background" >
+    android:fitsSystemWindows="false"
+    android:background="@color/all_set_page_background">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <com.airbnb.lottie.LottieAnimationView
+        android:id="@+id/animated_background"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:id="@+id/content_view"
-        android:fitsSystemWindows="false">
+        android:gravity="center"
+        android:scaleType="centerCrop"
+        app:lottie_autoPlay="true"
+        app:lottie_loop="true"
 
-        <com.airbnb.lottie.LottieAnimationView
-            android:id="@+id/animated_background"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:gravity="center"
-            android:scaleType="centerCrop"
-            app:lottie_autoPlay="true"
-            app:lottie_loop="true" />
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fillViewport="true"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
 
         <androidx.constraintlayout.widget.ConstraintLayout
             android:id="@+id/text_content_view"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/allset_page_margin_horizontal"
             android:layout_marginEnd="@dimen/allset_page_margin_horizontal"
             android:layoutDirection="locale"
@@ -78,10 +88,24 @@
                 app:layout_constraintStart_toStartOf="parent"
                 android:gravity="start"/>
 
-            <include layout="@layout/allset_navigation_and_hint"/>
+            <include layout="@layout/allset_navigation"/>
+
+            <TextView
+                android:id="@+id/hint"
+                style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="24dp"
+                android:text="@string/allset_hint"
+                android:textSize="@dimen/allset_page_swipe_up_text_size"
+                android:gravity="center_horizontal"
+
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent" />
 
         </androidx.constraintlayout.widget.ConstraintLayout>
 
-    </androidx.constraintlayout.widget.ConstraintLayout>
+    </ScrollView>
 
-</FrameLayout>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml b/quickstep/res/layout/allset_navigation.xml
similarity index 76%
rename from quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
rename to quickstep/res/layout/allset_navigation.xml
index 44b3ecb..76b24af 100644
--- a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
+++ b/quickstep/res/layout/allset_navigation.xml
@@ -22,21 +22,15 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="24dp"
+        android:layout_marginBottom="24dp"
         android:background="?android:attr/selectableItemBackground"
         android:minHeight="48dp"
         android:text="@string/allset_navigation_settings"
-        app:layout_constraintTop_toBottomOf="@id/subtitle"
-        app:layout_constraintStart_toStartOf="parent" />
+        android:gravity="center_horizontal"
 
-    <TextView
-        android:id="@+id/hint"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="24dp"
-        android:text="@string/allset_hint"
-        android:textSize="@dimen/allset_page_swipe_up_text_size"
-        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintTop_toBottomOf="@id/subtitle"
+        app:layout_constraintBottom_toTopOf="@id/hint"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent" />
 
diff --git a/quickstep/res/layout/allset_navigation_and_hint.xml b/quickstep/res/layout/allset_navigation_and_hint.xml
deleted file mode 100644
index 4d5cf01..0000000
--- a/quickstep/res/layout/allset_navigation_and_hint.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/navigation_settings_guideline_bottom"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.83" />
-
-    <TextView
-        android:id="@+id/navigation_settings"
-        style="@style/TextAppearance.GestureTutorial.LinkText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="?android:attr/selectableItemBackground"
-        android:minHeight="48dp"
-        android:text="@string/allset_navigation_settings"
-        app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/hint_guideline_bottom"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.94" />
-
-    <TextView
-        android:id="@+id/hint"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/allset_hint"
-        android:textSize="@dimen/allset_page_swipe_up_text_size"
-        app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent" />
-
-</merge>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 0592510..c7325ba 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -2042,10 +2042,5 @@
             setCrossWindowBlursEnabled(
                     CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled());
         }
-
-        @Override
-        public void setSurface(SurfaceControl surface) {
-            super.setSurface(surface);
-        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 9afcd2a..d379d6d 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -159,6 +159,12 @@
     }
 
     @Override
+    protected void onInvalidSurface() {
+        // Lets wait for surface to become valid again
+        mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+    }
+
+    @Override
     public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
         mIgnoreStateChangesDuringMultiWindowAnimation = true;
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index ae1c979..072fc30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -137,7 +137,6 @@
         // Hide all desktop tasks and show them on the hidden tile
         int hiddenDesktopTasks = 0;
         if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
-            // TODO(b/280468885): show desktop task as a grouped desktop tile
             DesktopTask desktopTask = findDesktopTask(tasks);
             if (desktopTask != null) {
                 hiddenDesktopTasks = desktopTask.tasks.size();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index 2517ff6..c9d331b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -41,8 +41,10 @@
     public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2;
     // Taskbar EDU overlay is open above the Taskbar. */
     public static final int FLAG_AUTOHIDE_SUSPEND_EDU_OPEN = 1 << 3;
-    // Taskbar in immersive mode in overview
+    // Taskbar is in immersive mode in overview.
     public static final int FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER = 1 << 4;
+    // Transient Taskbar is temporarily unstashed (pending a timeout).
+    public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5;
 
     @IntDef(flag = true, value = {
             FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
@@ -50,6 +52,7 @@
             FLAG_AUTOHIDE_SUSPEND_TOUCHING,
             FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
             FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
+            FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutohideSuspendFlag {}
@@ -85,18 +88,21 @@
 
         boolean isSuspended = isSuspended();
         mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended);
-        mActivity.onTransientAutohideSuspendFlagChanged(isSuspended);
+        mActivity.onTransientAutohideSuspendFlagChanged(isTransientTaskbarStashingSuspended());
     }
 
     /**
-     * Returns true iff taskbar autohide is currently suspended.
+     * Returns true iff taskbar autohide is currently suspended for immersive mode.
      */
-    public boolean isSuspended() {
+    private boolean isSuspended() {
         return mAutohideSuspendFlags != 0;
     }
 
-    public boolean isSuspendedForTransientTaskbarInOverview() {
-        return (mAutohideSuspendFlags & FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER) != 0;
+    /**
+     * Returns whether Transient Taskbar should avoid auto-stashing.
+     */
+    public boolean isTransientTaskbarStashingSuspended() {
+        return (mAutohideSuspendFlags & ~FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR) != 0;
     }
 
     @Override
@@ -115,6 +121,8 @@
         appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, "FLAG_AUTOHIDE_SUSPEND_EDU_OPEN");
         appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
                 "FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER");
+        appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
+                "FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR");
         return str.toString();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 24d2b0b..87538f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -519,10 +519,8 @@
             return;
         }
 
-        if (stash && mControllers.taskbarAutohideSuspendController.isSuspended()
-                && !mControllers.taskbarAutohideSuspendController
-                .isSuspendedForTransientTaskbarInOverview()) {
-            // Avoid stashing if autohide is currently suspended.
+        if (stash && mControllers.taskbarAutohideSuspendController
+                .isTransientTaskbarStashingSuspended()) {
             return;
         }
 
@@ -1080,6 +1078,9 @@
             mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)
                     ? LAUNCHER_TRANSIENT_TASKBAR_HIDE
                     : LAUNCHER_TRANSIENT_TASKBAR_SHOW);
+            mControllers.taskbarAutohideSuspendController.updateFlag(
+                    TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
+                    !hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
         }
     }
 
@@ -1172,7 +1173,7 @@
     }
 
     private void onTaskbarTimeout(Alarm alarm) {
-        if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
+        if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) {
             return;
         }
         updateAndAnimateTransientTaskbarForTimeout();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index bc1a2c8..bf3b932 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -18,6 +18,8 @@
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -128,12 +130,13 @@
 
         int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
         int actualIconSize = mActivityContext.getDeviceProfile().taskbarIconSize;
+        int visualIconSize = (int) (actualIconSize * ICON_VISIBLE_AREA_FACTOR);
 
         mIconTouchSize = Math.max(actualIconSize,
                 resources.getDimensionPixelSize(R.dimen.taskbar_icon_min_touch_size));
 
         // We layout the icons to be of mIconTouchSize in width and height
-        mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
+        mItemMarginLeftRight = actualMargin - (mIconTouchSize - visualIconSize) / 2;
         mItemPadding = (mIconTouchSize - actualIconSize) / 2;
 
         mFolderLeaveBehindColor = Themes.getAttrColor(mActivityContext,
@@ -208,6 +211,8 @@
     }
 
     protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
+        // set taskbar pane title so that accessibility service know it window and focuses.
+        setAccessibilityPaneTitle(getContext().getString(R.string.taskbar_a11y_title));
         mControllerCallbacks = callbacks;
         mIconClickListener = mControllerCallbacks.getIconOnClickListener();
         mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ddaec06..ecc8e19 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -519,7 +519,7 @@
             }
             case QUICK_SWITCH_STATE_ORDINAL: {
                 RecentsView rv = getOverviewPanel();
-                TaskView tasktolaunch = rv.getTaskViewAt(0);
+                TaskView tasktolaunch = rv.getCurrentPageTaskView();
                 if (tasktolaunch != null) {
                     tasktolaunch.launchTask(success -> {
                         if (!success) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index a4db375..f6a25ce 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 
@@ -110,7 +111,19 @@
 
     @Override
     public LauncherState getHistoryForState(LauncherState previousState) {
-        return previousState == OVERVIEW ? OVERVIEW : NORMAL;
+        return previousState == BACKGROUND_APP ? QUICK_SWITCH_FROM_HOME
+                : previousState == OVERVIEW ? OVERVIEW : NORMAL;
+    }
+
+    @Override
+    public float[] getOverviewScaleAndOffset(Launcher launcher) {
+        if (!FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
+            return super.getOverviewScaleAndOffset(launcher);
+        }
+        // This handles the case of returning to the previous app from Overview -> All Apps gesture.
+        // This is the start scale/offset of overview that will be used for that transition.
+        // TODO (b/283336332): Translate in Y direction (ideally with overview resistance).
+        return new float[] {0.5f /* scale */, NO_OFFSET};
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index bb74a36..454a1f5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.AllAppsSwipeController;
@@ -92,7 +93,9 @@
     @Override
     protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
         if (fromState == ALL_APPS && !isDragTowardPositive) {
-            return NORMAL;
+            return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()
+                    ? mLauncher.getStateManager().getLastState()
+                    : NORMAL;
         } else if (fromState == OVERVIEW) {
             return isDragTowardPositive ? OVERVIEW : NORMAL;
         } else if (fromState == NORMAL && isDragTowardPositive) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index bf4896d..0bf82cf 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -831,7 +831,8 @@
      * @return Whether we can create the launcher controller or update its progress.
      */
     private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
-        return mGestureState.getEndTarget() != HOME && !mHasEndedLauncherTransition;
+        return mGestureState.getEndTarget() != HOME
+                && !mHasEndedLauncherTransition && mActivity != null;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 84b90b9..27fb476 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -16,6 +16,7 @@
 
 package com.android.quickstep;
 
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.RemoteAnimationTarget;
@@ -123,7 +124,14 @@
             mRemoteTargetHandles = newHandles;
         }
 
+        boolean containsSplitTargets = Arrays.stream(targets.apps)
+                .anyMatch(remoteAnimationTarget ->
+                        remoteAnimationTarget.windowConfiguration.getWindowingMode()
+                                == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+
         if (mRemoteTargetHandles.length == 1) {
+            // Single fullscreen app
+
             // If we're not in split screen, the splitIds count doesn't really matter since we
             // should always hit this case.
             mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
@@ -131,13 +139,29 @@
                 // Unclear why/when target.apps length == 0, but it sure does happen :(
                 mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(targets.apps[0], null);
             }
+        } else if (!containsSplitTargets) {
+            // Single App + Assistant
+            for (int i = 0; i < mRemoteTargetHandles.length; i++) {
+                mRemoteTargetHandles[i].mTransformParams.setTargetSet(targets);
+                mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(targets.apps[i], null);
+            }
         } else {
-            RemoteAnimationTarget topLeftTarget = targets.apps[0];
+            // Split apps (+ maybe assistant)
+            RemoteAnimationTarget topLeftTarget = Arrays.stream(targets.apps)
+                    .filter(remoteAnimationTarget ->
+                            remoteAnimationTarget.windowConfiguration.getWindowingMode()
+                                    == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
+                    .findFirst().get();
 
             // Fetch the adjacent target for split screen.
             RemoteAnimationTarget bottomRightTarget = null;
-            for (int i = 1; i < targets.apps.length; i++) {
+            for (int i = 0; i < targets.apps.length; i++) {
                 final RemoteAnimationTarget target = targets.apps[i];
+                if (target.windowConfiguration.getWindowingMode() !=
+                        WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW ||
+                        target == topLeftTarget) {
+                    continue;
+                }
                 Rect topLeftBounds = getStartBounds(topLeftTarget);
                 Rect bounds = getStartBounds(target);
                 if (topLeftBounds.left > bounds.right || topLeftBounds.top > bounds.bottom) {
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 76b4878..42b0edf 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -95,15 +95,11 @@
 
     private static final float ANIMATION_PAUSE_ALPHA_THRESHOLD = 0.1f;
 
-    private final Rect mTempSettingsBounds = new Rect();
-    private final Rect mTempInclusionBounds = new Rect();
-    private final Rect mTempExclusionBounds = new Rect();
-
     private TISBindHelper mTISBindHelper;
 
     private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
     private BgDrawable mBackground;
-    private View mContentView;
+    private View mRootView;
     private float mSwipeUpShift;
 
     @Nullable private Vibrator mVibrator;
@@ -116,7 +112,8 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_allset);
-        findViewById(R.id.root_view).setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+        mRootView = findViewById(R.id.root_view);
+        mRootView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
 
@@ -131,8 +128,7 @@
         ((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(accentColor);
 
         mBackground = new BgDrawable(this);
-        findViewById(R.id.root_view).setBackground(mBackground);
-        mContentView = findViewById(R.id.content_view);
+        mRootView.setBackground(mBackground);
         mSwipeUpShift = resources.getDimension(R.dimen.allset_swipe_up_shift);
 
         TextView subtitle = findViewById(R.id.subtitle);
@@ -160,34 +156,6 @@
         }
         hint.setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
 
-        View textContent = findViewById(R.id.text_content_view);
-        textContent.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    mTempSettingsBounds.set(
-                            settings.getLeft(),
-                            settings.getTop(),
-                            settings.getRight(),
-                            settings.getBottom());
-                    mTempInclusionBounds.set(
-                            0,
-                            // Do not allow overlapping with the subtitle text
-                            subtitle.getBottom(),
-                            textContent.getWidth(),
-                            textContent.getHeight());
-                    mTempExclusionBounds.set(
-                            hint.getLeft(),
-                            hint.getTop(),
-                            hint.getRight(),
-                            hint.getBottom());
-
-                    Utilities.translateOverlappingView(
-                            settings,
-                            mTempSettingsBounds,
-                            mTempInclusionBounds,
-                            mTempExclusionBounds,
-                            Utilities.TRANSLATE_UP);
-                });
-
         mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
 
         mVibrator = getSystemService(Vibrator.class);
@@ -356,8 +324,8 @@
     private void onSwipeProgressUpdate() {
         mBackground.setProgress(mSwipeProgress.value);
         float alpha = getContentViewAlphaForSwipeProgress();
-        mContentView.setAlpha(alpha);
-        mContentView.setTranslationY((alpha - 1) * mSwipeUpShift);
+        mRootView.setAlpha(alpha);
+        mRootView.setTranslationY((alpha - 1) * mSwipeUpShift);
 
         TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
         if (mLauncherStartAnim == null && taskbarManager != null) {
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 23cfb39..931e468 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -88,6 +88,8 @@
      */
     protected boolean mInEarlyWakeUp;
 
+    private boolean mWaitingOnSurfaceValidity;
+
     public BaseDepthController(Launcher activity) {
         mLauncher = activity;
         mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
@@ -115,6 +117,8 @@
         }
     }
 
+    protected void onInvalidSurface() { }
+
     protected void applyDepthAndBlur() {
         float depth = mDepth;
         IBinder windowToken = mLauncher.getRootView().getWindowToken();
@@ -128,9 +132,15 @@
         if (!BlurUtils.supportsBlursOnWindows()) {
             return;
         }
-        if (mSurface == null || !mSurface.isValid()) {
+        if (mSurface == null) {
             return;
         }
+        if (!mSurface.isValid()) {
+            mWaitingOnSurfaceValidity = true;
+            onInvalidSurface();
+            return;
+        }
+        mWaitingOnSurfaceValidity = false;
         boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
         boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg && !mPauseBlurs;
 
@@ -174,7 +184,7 @@
      * Sets the specified app target surface to apply the blur to.
      */
     protected void setSurface(SurfaceControl surface) {
-        if (mSurface != surface) {
+        if (mSurface != surface || mWaitingOnSurfaceValidity) {
             mSurface = surface;
             applyDepthAndBlur();
         }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index 0631537..e073264 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -320,7 +320,7 @@
             return SPLIT_SINGLE_SHORTCUT_FULLSCREEN
         }
 
-        if (initialIntent != null) {
+        if (initialPendingIntent != null) {
             return SPLIT_SINGLE_INTENT_FULLSCREEN
         }
         throw IllegalStateException("Unidentified fullscreen launch type")
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 7f035a2..48dadd1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -410,7 +410,7 @@
             // transitions, the animation leashes are reparented to an animation container so we
             // can bump layers as needed.
             builder.setLayer(mDrawsBelowRecents
-                    ? Integer.MIN_VALUE + 1
+                    ? Integer.MIN_VALUE + app.prefixOrderIndex
                     : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 7cd6756..3e79eaf 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -92,6 +92,8 @@
 
     private Task mTask;
     private boolean mHasLimit;
+
+    private long mAppUsageLimitTimeMs;
     private long mAppRemainingTimeMs;
     @Nullable
     private View mBanner;
@@ -113,10 +115,12 @@
         mHasLimit = false;
         mTaskView.setContentDescription(mTask.titleDescription);
         replaceBanner(null);
-        mAppRemainingTimeMs = 0;
+        mAppUsageLimitTimeMs = -1;
+        mAppRemainingTimeMs = -1;
     }
 
     private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
+        mAppUsageLimitTimeMs = appUsageLimitTimeMs;
         mAppRemainingTimeMs = appRemainingTimeMs;
         mHasLimit = true;
         TextView toast = mActivity.getViewCache().getView(R.layout.digital_wellbeing_toast,
@@ -138,11 +142,12 @@
     }
 
     public void initialize(Task task) {
+        mAppUsageLimitTimeMs = mAppRemainingTimeMs = -1;
         mTask = task;
         THREAD_POOL_EXECUTOR.execute(() -> {
             final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit(
-                    task.getTopComponent().getPackageName(),
-                    UserHandle.of(task.key.userId));
+                    mTask.getTopComponent().getPackageName(),
+                    UserHandle.of(mTask.key.userId));
 
             final long appUsageLimitTimeMs =
                     usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
@@ -275,6 +280,10 @@
         }
     }
 
+    public String getContentDescription() {
+            return getContentDescriptionForTask(mTask, mAppUsageLimitTimeMs, mAppRemainingTimeMs);
+    }
+
     private String getContentDescriptionForTask(
             Task task, long appUsageLimitTimeMs, long appRemainingTimeMs) {
         return appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0 ?
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f4b3a32..b9e6d14 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4104,6 +4104,15 @@
         }
         alpha = Utilities.boundToRange(alpha, 0, 1);
         mContentAlpha = alpha;
+
+        runActionOnRemoteHandles(remoteTargetHandle -> {
+            TransformParams params = remoteTargetHandle.getTransformParams();
+            params.setTargetAlpha(mContentAlpha);
+            if (params.getTargetSet() != null) {
+                remoteTargetHandle.getTaskViewSimulator().apply(params);
+            }
+        });
+
         int runningTaskId = getTaskIdsForRunningTaskView()[0];
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
             TaskView child = requireTaskViewAt(i);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 46dd94b..ea45f9d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1343,6 +1343,7 @@
 
     protected void refreshTaskThumbnailSplash() {
         mSnapshotView.refreshSplashView();
+        setContentDescription(mDigitalWellBeingToast.getContentDescription());
     }
 
     private void setSplitSelectTranslationX(float x) {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 97e34c5..083b4f8 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -62,6 +62,7 @@
 import com.android.launcher3.util.rule.SamplerRule;
 import com.android.launcher3.util.rule.ScreenRecordRule;
 import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.ViewCaptureAnalysisRule;
 import com.android.launcher3.util.rule.ViewCaptureRule;
 import com.android.quickstep.views.RecentsView;
 
@@ -121,7 +122,8 @@
                 .outerRule(new SamplerRule())
                 .around(new NavigationModeSwitchRule(mLauncher))
                 .around(viewCaptureRule)
-                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
+                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()))
+                .around(new ViewCaptureAnalysisRule(viewCaptureRule.getViewCapture()));
 
         mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
                 getHomeIntentInPackage(context),
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 275a3b8..80df78a 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -204,6 +204,7 @@
         <!-- 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 -->
@@ -251,10 +252,11 @@
         <attr name="maxAvailableSize" format="dimension" />
     </declare-styleable>
 
-    <declare-styleable name="SpecSize">
+    <declare-styleable name="SizeSpec">
         <attr name="fixedSize" format="dimension" />
         <attr name="ofAvailableSpace" format="float" />
         <attr name="ofRemainderSpace" format="float" />
+        <attr name="matchWorkspace" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="ProfileDisplayOption">
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c08dd1d..22d393a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
 import static com.android.launcher3.testing.shared.ResourceUtils.roundPxValueFromFloat;
@@ -346,8 +347,8 @@
         if (DisplayController.isTransientTaskbar(context)) {
             float invTransientIconSizeDp = inv.transientTaskbarIconSize[mTypeIndex];
             taskbarIconSize = pxFromDp(invTransientIconSizeDp, mMetrics);
-            taskbarHeight = taskbarIconSize
-                    + (2 * res.getDimensionPixelSize(R.dimen.transient_taskbar_padding));
+            taskbarHeight = Math.round((taskbarIconSize * ICON_VISIBLE_AREA_FACTOR)
+                    + (2 * res.getDimensionPixelSize(R.dimen.transient_taskbar_padding)));
             stashedTaskbarHeight =
                     res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_height);
             taskbarBottomMargin =
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index 3e55425..f15dc83 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -94,6 +94,9 @@
                         // folder
                         CellLayout cellLayout = mLauncher.getCellLayout(info.container,
                                 info.screenId);
+                        if (cellLayout == null) {
+                            return;
+                        }
                         finalItem =  info.contents.remove(0);
                         newIcon = mLauncher.createShortcut(cellLayout, finalItem);
                         mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
new file mode 100644
index 0000000..bf5ca1c
--- /dev/null
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -0,0 +1,73 @@
+package com.android.launcher3.responsive
+
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import android.util.Log
+import android.util.TypedValue
+import com.android.launcher3.R
+import com.android.launcher3.util.ResourceHelper
+
+data class SizeSpec(
+    val fixedSize: Float,
+    val ofAvailableSpace: Float,
+    val ofRemainderSpace: Float,
+    val matchWorkspace: Boolean
+) {
+
+    fun isValid(): Boolean {
+        // All attributes are empty
+        if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) {
+            Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
+            return false
+        }
+
+        // More than one attribute is filled
+        val attrCount =
+            (if (fixedSize > 0) 1 else 0) +
+                (if (ofAvailableSpace > 0) 1 else 0) +
+                (if (ofRemainderSpace > 0) 1 else 0) +
+                (if (matchWorkspace) 1 else 0)
+        if (attrCount > 1) {
+            Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
+            return false
+        }
+
+        // Values should be between 0 and 1
+        if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
+            Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
+            return false
+        }
+
+        // Invalid fixed size
+        if (fixedSize < 0f) {
+            Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.")
+            return false
+        }
+
+        return true
+    }
+
+    companion object {
+        private const val TAG = "WorkspaceSpecs::SizeSpec"
+        private fun getValue(a: TypedArray, index: Int): Float {
+            return when (a.getType(index)) {
+                TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat()
+                TypedValue.TYPE_FLOAT -> a.getFloat(index, 0f)
+                else -> 0f
+            }
+        }
+
+        fun create(resourceHelper: ResourceHelper, attrs: AttributeSet): SizeSpec {
+            val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SizeSpec)
+
+            val fixedSize = getValue(styledAttrs, R.styleable.SizeSpec_fixedSize)
+            val ofAvailableSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofAvailableSpace)
+            val ofRemainderSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofRemainderSpace)
+            val matchWorkspace = styledAttrs.getBoolean(R.styleable.SizeSpec_matchWorkspace, false)
+
+            styledAttrs.recycle()
+
+            return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace)
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 6573691..348c8d8 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -40,17 +40,14 @@
     public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
     public static final String SEARCH_KEYBOARD_EDU_SEEN = "launcher.search_edu_seen";
     public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
-    public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count";
     public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
-    public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss";
     public static final String TASKBAR_EDU_TOOLTIP_STEP = "launcher.taskbar_edu_tooltip_step";
     // When adding a new key, add it here as well, to be able to reset it from Developer Options.
     public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
             "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
             "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
                     HOTSEAT_LONGPRESS_TIP_SEEN },
-            "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT,
-                    SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED},
+            "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT},
             "Taskbar Education", new String[] { TASKBAR_EDU_TOOLTIP_STEP },
             "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
     );
@@ -62,7 +59,6 @@
             HOME_BOUNCE_SEEN,
             HOTSEAT_LONGPRESS_TIP_SEEN,
             SEARCH_KEYBOARD_EDU_SEEN,
-            QSB_SEARCH_ONBOARDING_CARD_DISMISSED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventBoolKey {}
@@ -74,7 +70,6 @@
             HOME_BOUNCE_COUNT,
             HOTSEAT_DISCOVERY_TIP_COUNT,
             SEARCH_SNACKBAR_COUNT,
-            SEARCH_ONBOARDING_COUNT,
             ALL_APPS_VISITED_COUNT,
             TASKBAR_EDU_TOOLTIP_STEP,
     })
@@ -88,8 +83,6 @@
         maxCounts.put(HOME_BOUNCE_COUNT, 3);
         maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
         maxCounts.put(SEARCH_SNACKBAR_COUNT, 3);
-        // This is the sum of all onboarding cards. Currently there is only 1 card shown 3 times.
-        maxCounts.put(SEARCH_ONBOARDING_COUNT, 3);
         maxCounts.put(ALL_APPS_VISITED_COUNT, 20);
         maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2);
         MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index ceba0db..91945ca 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -17,6 +17,7 @@
 
 import static android.os.VibrationEffect.createPredefined;
 import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
+
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
@@ -68,6 +69,9 @@
     @Nullable
     private final VibrationEffect mBumpEffect;
 
+    @Nullable
+    private final VibrationEffect mAssistEffect;
+
     private long mLastDragTime;
     private final int mThresholdUntilNextDragCallMillis;
 
@@ -125,12 +129,25 @@
             mBumpEffect = null;
             mThresholdUntilNextDragCallMillis = 0;
         }
+
+        if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported(
+                VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+                VibrationEffect.Composition.PRIMITIVE_TICK)) {
+            // quiet ramp, short pause, then sharp tick
+            mAssistEffect = VibrationEffect.startComposition()
+                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+                    .compose();
+        } else {
+            // fallback for devices without composition support
+            mAssistEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
+        }
     }
 
     /**
-     *  This is called when the user swipes to/from all apps. This is meant to be used in between
-     *  long animation progresses so that it gives a dragging texture effect. For a better
-     *  experience, this should be used in combination with vibrateForDragCommit().
+     * This is called when the user swipes to/from all apps. This is meant to be used in between
+     * long animation progresses so that it gives a dragging texture effect. For a better
+     * experience, this should be used in combination with vibrateForDragCommit().
      */
     public void vibrateForDragTexture() {
         if (mDragEffect == null) {
@@ -145,7 +162,7 @@
     }
 
     /**
-     *  This is used when user reaches the commit threshold when swiping to/from from all apps.
+     * This is used when user reaches the commit threshold when swiping to/from from all apps.
      */
     public void vibrateForDragCommit() {
         if (mCommitEffect != null) {
@@ -156,9 +173,9 @@
     }
 
     /**
-     *  The bump haptic is used to be called at the end of a swipe and only if it the gesture is a
-     *  FLING going to/from all apps. Client can just call this method elsewhere just for the
-     *  effect.
+     * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a
+     * FLING going to/from all apps. Client can just call this method elsewhere just for the
+     * effect.
      */
     public void vibrateForDragBump() {
         if (mBumpEffect != null) {
@@ -167,6 +184,15 @@
     }
 
     /**
+     * The assist haptic is used to be called when an assistant is invoked
+     */
+    public void vibrateForAssist() {
+        if (mAssistEffect != null) {
+            vibrate(mAssistEffect);
+        }
+    }
+
+    /**
      * This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For
      * example, when no animation is happening but a vibrator happens to be vibrating still. Need
      * boolean parameter for {@link PendingAnimation#addEndListener(Consumer)}.
@@ -176,6 +202,7 @@
         // reset dragTexture timestamp to be able to play dragTexture again whenever cancelled
         mLastDragTime = 0;
     }
+
     private boolean isHapticFeedbackEnabled(ContentResolver resolver) {
         return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1;
     }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 33c4f8d..a3ef6e1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -774,7 +774,7 @@
         super.onCloseComplete();
         removeCallbacks(mShowEducationTipTask);
         if (mLatestEducationalTip != null) {
-            mLatestEducationalTip.close(false);
+            mLatestEducationalTip.close(true);
         }
         AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
     }
diff --git a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
index dc5ae47..4815924 100644
--- a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
+++ b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
@@ -16,13 +16,12 @@
 
 package com.android.launcher3.workspace
 
-import android.content.res.TypedArray
 import android.content.res.XmlResourceParser
 import android.util.AttributeSet
 import android.util.Log
-import android.util.TypedValue
 import android.util.Xml
 import com.android.launcher3.R
+import com.android.launcher3.responsive.SizeSpec
 import com.android.launcher3.util.ResourceHelper
 import java.io.IOException
 import kotlin.math.roundToInt
@@ -95,16 +94,16 @@
                                 if (type == XmlPullParser.START_TAG) {
                                     when (parser.name) {
                                         XmlTags.START_PADDING -> {
-                                            startPadding = SizeSpec(resourceHelper, attr)
+                                            startPadding = SizeSpec.create(resourceHelper, attr)
                                         }
                                         XmlTags.END_PADDING -> {
-                                            endPadding = SizeSpec(resourceHelper, attr)
+                                            endPadding = SizeSpec.create(resourceHelper, attr)
                                         }
                                         XmlTags.GUTTER -> {
-                                            gutter = SizeSpec(resourceHelper, attr)
+                                            gutter = SizeSpec.create(resourceHelper, attr)
                                         }
                                         XmlTags.CELL_SIZE -> {
-                                            cellSize = SizeSpec(resourceHelper, attr)
+                                            cellSize = SizeSpec.create(resourceHelper, attr)
                                         }
                                     }
                                 }
@@ -270,61 +269,12 @@
     }
 
     private fun allSpecsAreValid(): Boolean =
-        startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
-}
-
-class SizeSpec(resourceHelper: ResourceHelper, attrs: AttributeSet) {
-    val fixedSize: Float
-    val ofAvailableSpace: Float
-    val ofRemainderSpace: Float
-
-    init {
-        val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SpecSize)
-
-        fixedSize = getValue(styledAttrs, R.styleable.SpecSize_fixedSize)
-        ofAvailableSpace = getValue(styledAttrs, R.styleable.SpecSize_ofAvailableSpace)
-        ofRemainderSpace = getValue(styledAttrs, R.styleable.SpecSize_ofRemainderSpace)
-
-        styledAttrs.recycle()
-    }
-
-    private fun getValue(a: TypedArray, index: Int): Float {
-        if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
-            return a.getDimensionPixelSize(index, 0).toFloat()
-        } else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
-            return a.getFloat(index, 0f)
-        }
-        return 0f
-    }
-
-    fun isValid(): Boolean {
-        // All attributes are empty
-        if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f) {
-            Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
-            return false
-        }
-
-        // More than one attribute is filled
-        val attrCount =
-            (if (fixedSize > 0) 1 else 0) +
-                (if (ofAvailableSpace > 0) 1 else 0) +
-                (if (ofRemainderSpace > 0) 1 else 0)
-        if (attrCount > 1) {
-            Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
-            return false
-        }
-
-        // Values should be between 0 and 1
-        if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
-            Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
-            return false
-        }
-
-        return true
-    }
-
-    override fun toString(): String {
-        return "SizeSpec(fixedSize=$fixedSize, ofAvailableSpace=$ofAvailableSpace, " +
-            "ofRemainderSpace=$ofRemainderSpace)"
-    }
+        startPadding.isValid() &&
+            endPadding.isValid() &&
+            gutter.isValid() &&
+            cellSize.isValid() &&
+            !startPadding.matchWorkspace &&
+            !endPadding.matchWorkspace &&
+            !gutter.matchWorkspace &&
+            !cellSize.matchWorkspace
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index e7f4084..d518a0e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -58,6 +58,7 @@
       "src/com/android/launcher3/util/rule/SimpleActivityRule.java",
       "src/com/android/launcher3/util/rule/TestStabilityRule.java",
       "src/com/android/launcher3/util/rule/TISBindRule.java",
+      "src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java",
       "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
       "src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
       "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml
index 2310d9e..cb6da3b 100644
--- a/tests/res/values/attrs.xml
+++ b/tests/res/values/attrs.xml
@@ -26,10 +26,10 @@
         <attr name="maxAvailableSize" format="dimension" />
     </declare-styleable>
 
-    <declare-styleable name="SpecSize">
+    <declare-styleable name="SizeSpec">
         <attr name="fixedSize" format="dimension" />
         <attr name="ofAvailableSpace" format="float" />
         <attr name="ofRemainderSpace" format="float" />
+        <attr name="matchWorkspace" format="boolean" />
     </declare-styleable>
-
 </resources>
diff --git a/tests/res/xml/invalid_workspace_file_case_4.xml b/tests/res/xml/invalid_workspace_file_case_4.xml
new file mode 100644
index 0000000..9e74c85
--- /dev/null
+++ b/tests/res/xml/invalid_workspace_file_case_4.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <workspaceSpec
+        launcher:specType="height"
+        launcher:maxAvailableSize="648dp">
+        <startPadding
+            launcher:ofAvailableSpace="0.0125" />
+        <endPadding
+            launcher:ofAvailableSpace="0.05" />
+        <!--  value in workspace spec using matchWorkspace -->
+        <gutter
+            launcher:matchWorkspace="true" />
+        <cellSize
+            launcher:ofRemainderSpace="0.2" />
+    </workspaceSpec>
+
+    <workspaceSpec
+        launcher:specType="height"
+        launcher:maxAvailableSize="9999dp">
+        <startPadding
+            launcher:ofAvailableSpace="0.0306" />
+        <endPadding
+            launcher:ofAvailableSpace="0.068" />
+        <gutter
+            launcher:fixedSize="16dp" />
+        <cellSize
+            launcher:ofRemainderSpace="0.2" />
+    </workspaceSpec>
+
+    <!-- Width spec is always the same -->
+    <workspaceSpec
+        launcher:specType="width"
+        launcher:maxAvailableSize="9999dp">
+        <startPadding
+            launcher:ofRemainderSpace="0.21436227" />
+        <endPadding
+            launcher:ofRemainderSpace="0.21436227" />
+        <gutter
+            launcher:ofRemainderSpace="0.11425509" />
+        <cellSize
+            launcher:fixedSize="120dp" />
+    </workspaceSpec>
+</workspaceSpecs>
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 7f2d4a0..8cc01ff 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -155,6 +155,7 @@
     public static final String PERMANENT_DIAG_TAG = "TaplTarget";
     public static final String WORK_TAB_MISSING = "b/243688989";
     public static final String TWO_TASKBAR_LONG_CLICKS = "b/262282528";
+    public static final String FLAKY_ACTIVITY_COUNT = "b/260260325";
 
     public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
     public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
new file mode 100644
index 0000000..426777d
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SizeSpecTest : AbstractDeviceProfileTest() {
+    override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+    @Before
+    fun setup() {
+        initializeVarsForPhone(deviceSpecs["phone"]!!)
+    }
+
+    @Test
+    fun valid_values() {
+        val combinations =
+            listOf(
+                SizeSpec(100f, 0f, 0f, false),
+                SizeSpec(0f, 1f, 0f, false),
+                SizeSpec(0f, 0f, 1f, false),
+                SizeSpec(0f, 0f, 0f, false),
+                SizeSpec(0f, 0f, 0f, true)
+            )
+
+        for (instance in combinations) {
+            assertThat(instance.isValid()).isEqualTo(true)
+        }
+    }
+
+    @Test
+    fun multiple_values_assigned() {
+        val combinations =
+            listOf(
+                SizeSpec(1f, 1f, 0f, false),
+                SizeSpec(1f, 0f, 1f, false),
+                SizeSpec(1f, 0f, 0f, true),
+                SizeSpec(0f, 1f, 1f, false),
+                SizeSpec(0f, 1f, 0f, true),
+                SizeSpec(0f, 0f, 1f, true),
+                SizeSpec(1f, 1f, 1f, true)
+            )
+
+        for (instance in combinations) {
+            assertThat(instance.isValid()).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun invalid_values() {
+        val combinations =
+            listOf(
+                SizeSpec(0f, 1.1f, 0f, false),
+                SizeSpec(0f, -0.1f, 0f, false),
+                SizeSpec(0f, 0f, 1.1f, false),
+                SizeSpec(0f, 0f, -0.1f, false),
+                SizeSpec(-1f, 0f, 0f, false)
+            )
+
+        for (instance in combinations) {
+            assertThat(instance.isValid()).isEqualTo(false)
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 98c4c61..60f1418 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -69,6 +69,7 @@
 import com.android.launcher3.util.rule.ScreenRecordRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.ViewCaptureAnalysisRule;
 import com.android.launcher3.util.rule.ViewCaptureRule;
 
 import org.junit.After;
@@ -207,7 +208,8 @@
         final RuleChain inner = RuleChain
                 .outerRule(new PortraitLandscapeRunner(this))
                 .around(viewCaptureRule)
-                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
+                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()))
+                .around(new ViewCaptureAnalysisRule(viewCaptureRule.getViewCapture()));
 
         return TestHelpers.isInLauncherProcess()
                 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
diff --git a/tests/src/com/android/launcher3/util/TestResourceHelper.kt b/tests/src/com/android/launcher3/util/TestResourceHelper.kt
index fb03fe1..3f0054e 100644
--- a/tests/src/com/android/launcher3/util/TestResourceHelper.kt
+++ b/tests/src/com/android/launcher3/util/TestResourceHelper.kt
@@ -27,7 +27,7 @@
     ResourceHelper(context, specsFileId) {
     override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray {
         var clone = styleId.clone()
-        if (styleId == R.styleable.SpecSize) clone = TestR.styleable.SpecSize
+        if (styleId == R.styleable.SizeSpec) clone = TestR.styleable.SizeSpec
         else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec
         return context.obtainStyledAttributes(attrs, clone)
     }
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java b/tests/src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java
new file mode 100644
index 0000000..702757f
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureAnalysisRule.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util.rule;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.data.ExportedData;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * After the test succeeds, the rule looks for anomalies in the data accumulated by ViewCapture
+ * that's passed as a parameter. If anomalies are detected, throws an exception and fails the test.
+ */
+public class ViewCaptureAnalysisRule extends TestWatcher {
+    @NonNull
+    private final ViewCapture mViewCapture;
+
+    public ViewCaptureAnalysisRule(@NonNull ViewCapture viewCapture) {
+        mViewCapture = viewCapture;
+    }
+
+    @Override
+    protected void succeeded(Description description) {
+        super.succeeded(description);
+        try {
+            analyzeViewCaptureData(mViewCapture.getExportedData(
+                    InstrumentationRegistry.getTargetContext()));
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void analyzeViewCaptureData(ExportedData viewCaptureData) {
+    }
+}
diff --git a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt
index 9cd0a2e..51808e3 100644
--- a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt
+++ b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt
@@ -50,16 +50,20 @@
                     "specType=HEIGHT, " +
                     "startPadding=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "endPadding=SizeSpec(fixedSize=84.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "gutter=SizeSpec(fixedSize=42.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "cellSize=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.15808, " +
-                    "ofRemainderSpace=0.0)" +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false)" +
                     ")"
             )
         assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString())
@@ -69,16 +73,20 @@
                     "specType=HEIGHT, " +
                     "startPadding=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "endPadding=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=1.0), " +
+                    "ofRemainderSpace=1.0, " +
+                    "matchWorkspace=false), " +
                     "gutter=SizeSpec(fixedSize=42.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "cellSize=SizeSpec(fixedSize=273.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0)" +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false)" +
                     ")"
             )
         assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString())
@@ -88,16 +96,20 @@
                     "specType=HEIGHT, " +
                     "startPadding=SizeSpec(fixedSize=21.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "endPadding=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=1.0), " +
+                    "ofRemainderSpace=1.0, " +
+                    "matchWorkspace=false), " +
                     "gutter=SizeSpec(fixedSize=42.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "cellSize=SizeSpec(fixedSize=273.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0)" +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false)" +
                     ")"
             )
         assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1)
@@ -108,16 +120,20 @@
                     "specType=WIDTH, " +
                     "startPadding=SizeSpec(fixedSize=58.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "endPadding=SizeSpec(fixedSize=58.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "gutter=SizeSpec(fixedSize=42.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.0), " +
+                    "ofRemainderSpace=0.0, " +
+                    "matchWorkspace=false), " +
                     "cellSize=SizeSpec(fixedSize=0.0, " +
                     "ofAvailableSpace=0.0, " +
-                    "ofRemainderSpace=0.25)" +
+                    "ofRemainderSpace=0.25, " +
+                    "matchWorkspace=false)" +
                     ")"
             )
     }
@@ -136,4 +152,9 @@
     fun parseInvalidFile_valueBiggerThan1_throwsError() {
         WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3))
     }
+
+    @Test(expected = IllegalStateException::class)
+    fun parseInvalidFile_matchWorkspace_true_throwsError() {
+        WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4))
+    }
 }