Merge "Adding support for listening to end callbacks when launching an intent from Launcher" into udc-dev
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 0eef70e..a8b7698 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -57,7 +57,6 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.ActivityContext;
@@ -183,7 +182,16 @@
                 : null;
         super.applyFromWorkspaceItem(info, animate, staggerIndex);
         int oldPlateColor = mPlateColor;
-        int newPlateColor = ColorUtils.setAlphaComponent(mDotParams.appColor, 200);
+
+        int newPlateColor;
+        if (getIcon().isThemed()) {
+            newPlateColor = getResources().getColor(android.R.color.system_accent1_300);
+        } else {
+            float[] hctPlateColor = new float[3];
+            ColorUtils.colorToM3HCT(mDotParams.appColor, hctPlateColor);
+            newPlateColor = ColorUtils.M3HCTtoColor(hctPlateColor[0], 36, 85);
+        }
+
         if (!animate) {
             mPlateColor = newPlateColor;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 40dfd82..8cbd6e8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -41,11 +41,9 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.quickstep.views.RecentsView;
@@ -108,11 +106,6 @@
         if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
             return true;
         }
-        if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
-                && TopTaskTracker.INSTANCE.get(mLauncher).getCachedTopTask(false)
-                        .isExcludedAssistant()) {
-            return true;
-        }
         return false;
     }
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 99a57a2..cf68c91 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -24,7 +24,6 @@
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
-import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
@@ -1089,22 +1088,17 @@
         boolean hasWindowFocus = activity.getRootView().hasWindowFocus();
         boolean isPreviousGestureAnimatingToLauncher =
                 previousGestureState.isRunningAnimationToLauncher();
-        boolean forcingOverviewInputConsumer =
-                ASSISTANT_GIVES_LAUNCHER_FOCUS.get() && forceOverviewInputConsumer;
         boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode();
         reasonString.append(SUBSTRING_PREFIX)
                 .append(hasWindowFocus
                         ? "activity has window focus"
                         : (isPreviousGestureAnimatingToLauncher
                                 ? "previous gesture is still animating to launcher"
-                                : (forcingOverviewInputConsumer
-                                        ? "assistant gives launcher focus and forcing focus"
-                                        : (isInLiveTileMode
-                                                ? "device is in live mode"
-                                                : "all overview focus conditions failed"))));
+                                : isInLiveTileMode
+                                        ? "device is in live mode"
+                                        : "all overview focus conditions failed"));
         if (hasWindowFocus
                 || isPreviousGestureAnimatingToLauncher
-                || forcingOverviewInputConsumer
                 || isInLiveTileMode) {
             reasonString.append(SUBSTRING_PREFIX)
                     .append("overview should have focus, using OverviewInputConsumer");
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 64165b6..3388642 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -51,6 +51,7 @@
 
     private final boolean mStartingInActivityBounds;
     private boolean mTargetHandledTouch;
+    private boolean mHasSetTouchModeForFirstDPadEvent;
 
     public OverviewInputConsumer(GestureState gestureState, T activity,
             @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
@@ -95,6 +96,9 @@
                 mInputMonitor.pilferPointers();
             }
         }
+        if (mHasSetTouchModeForFirstDPadEvent) {
+            mActivity.getRootView().clearFocus();
+        }
     }
 
     @Override
@@ -112,6 +116,19 @@
                 mgr.dispatchVolumeKeyEventAsSystemService(ev,
                         AudioManager.USE_DEFAULT_STREAM_TYPE);
                 break;
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                if (!mHasSetTouchModeForFirstDPadEvent) {
+                    // When Overview is launched via meta+tab or swipe up from an app, the touch
+                    // mode somehow is not changed to false by the Android framework. The subsequent
+                    // key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched to focused
+                    // views, while focus can only be requested in
+                    // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To
+                    // note, here we launch overview with live tile.
+                    mHasSetTouchModeForFirstDPadEvent = true;
+                    mActivity.getRootView().getViewRootImpl().touchModeChanged(false);
+                }
+                break;
             default:
                 break;
         }
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 62d46d3..97e34c5 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.ViewCaptureRule;
 import com.android.quickstep.views.RecentsView;
 
 import org.junit.After;
@@ -115,10 +116,12 @@
             Utilities.enableRunningInTestHarnessForTests();
         }
 
+        final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
         mOrderSensitiveRules = RuleChain
                 .outerRule(new SamplerRule())
                 .around(new NavigationModeSwitchRule(mLauncher))
-                .around(new FailureWatcher(mDevice, mLauncher));
+                .around(viewCaptureRule)
+                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
 
         mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
                 getHomeIntentInPackage(context),
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 985fe77..0c036ff 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -28,6 +28,9 @@
     <color name="popup_notification_dot_light">@android:color/system_accent1_100</color>
     <color name="popup_notification_dot_dark">@android:color/system_accent2_600</color>
 
+    <color name="notification_dot_color_light">@android:color/system_accent3_200</color>
+    <color name="notification_dot_color_dark">@android:color/system_accent3_100</color>
+
     <color name="workspace_text_color_light">@android:color/system_neutral1_0</color>
     <color name="workspace_text_color_dark">@android:color/system_neutral1_1000</color>
 
@@ -40,7 +43,6 @@
 
     <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
 
-    <color name="folder_dot_color">@android:color/system_accent3_100</color>
     <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
     <color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
 
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8136534..418f5a7 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -40,8 +40,8 @@
     <attr name="eduHalfSheetBGColor" format="color" />
     <attr name="overviewScrimColor" format="color" />
     <attr name="popupNotificationDotColor" format="color" />
+    <attr name="notificationDotColor" format="color" />
 
-    <attr name="folderDotColor" format="color" />
     <attr name="folderPaginationColor" format="color" />
     <attr name="folderPreviewColor" format="color" />
     <attr name="folderBackgroundColor" format="color" />
@@ -90,7 +90,6 @@
     <declare-styleable name="FolderIconPreview">
         <attr name="folderPreviewColor" />
         <attr name="folderIconBorderColor" />
-        <attr name="folderDotColor" />
     </declare-styleable>
 
     <declare-styleable name="SearchResultSuggestion">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 9e73453..b1bff18 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -54,6 +54,9 @@
     <color name="popup_notification_dot_light">#FFF</color>
     <color name="popup_notification_dot_dark">#757575</color>
 
+    <color name="notification_dot_color_light">#6DD58C</color>
+    <color name="notification_dot_color_dark">#C4EED0</color>
+
     <color name="workspace_text_color_light">#FFF</color>
     <color name="workspace_text_color_dark">#FF000000</color>
 
@@ -66,7 +69,6 @@
     <color name="folder_preview_light">#F9F9F9</color>
     <color name="folder_preview_dark">#464746</color>
 
-    <color name="folder_dot_color">?attr/colorPrimary</color>
     <color name="folder_pagination_color_light">#ff006c5f</color>
     <color name="folder_pagination_color_dark">#ffbfebe3</color>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 11861b9..8f75550 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -41,6 +41,7 @@
         <item name="popupShadeSecond">@color/popup_shade_second_light</item>
         <item name="popupShadeThird">@color/popup_shade_third_light</item>
         <item name="popupNotificationDotColor">@color/popup_notification_dot_light</item>
+        <item name="notificationDotColor">@color/notification_dot_color_light</item>
         <item name="isMainColorDark">false</item>
         <item name="isWorkspaceDarkText">false</item>
         <item name="workspaceTextColor">@color/workspace_text_color_light</item>
@@ -49,7 +50,6 @@
         <item name="workspaceKeyShadowColor">#89000000</item>
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
-        <item name="folderDotColor">@color/folder_dot_color</item>
         <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
         <item name="folderPreviewColor">@color/folder_preview_light</item>
         <item name="folderBackgroundColor">@color/folder_background_light</item>
@@ -109,8 +109,8 @@
         <item name="popupShadeFirst">@color/popup_shade_first_dark</item>
         <item name="popupShadeSecond">@color/popup_shade_second_dark</item>
         <item name="popupShadeThird">@color/popup_shade_third_dark</item>
+        <item name="notificationDotColor">@color/notification_dot_color_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
-        <item name="folderDotColor">@color/folder_dot_color</item>
         <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
         <item name="folderPreviewColor">@color/folder_preview_dark</item>
         <item name="folderBackgroundColor">@color/folder_background_dark</item>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 450a9f0..f920d75 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -77,6 +77,7 @@
 import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.ShortcutUtil;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.IconLabelDotView;
 
@@ -387,8 +388,7 @@
         }
         FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
         mDotParams.appColor = iconDrawable.getIconColor();
-        mDotParams.dotColor = getContext().getResources()
-                .getColor(android.R.color.system_accent3_200, getContext().getTheme());
+        mDotParams.dotColor = Themes.getAttrColor(getContext(), R.attr.notificationDotColor);
         setIcon(iconDrawable);
         applyLabel(info);
     }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 1a86009..108e557 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -115,6 +115,12 @@
         setContentDescription(mText);
     }
 
+    protected void updateText(CharSequence text) {
+        setText(text);
+        mText = getText();
+        setContentDescription(mText);
+    }
+
     protected void setDrawable(int resId) {
         // We do not set the drawable in the xml as that inflates two drawables corresponding to
         // drawableLeft and drawableStart.
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 65870d3..617afcb 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -92,6 +92,15 @@
 
     static final String TAG = "Launcher.Model";
 
+    // Broadcast intent to track when the profile gets locked:
+    // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets
+    // locked when paused.
+    // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only
+    // appeared in Android S.
+    private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U
+            ? Intent.ACTION_PROFILE_INACCESSIBLE
+            : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
+
     @NonNull
     private final LauncherAppState mApp;
     @NonNull
@@ -290,9 +299,9 @@
             // If we have changed locale we need to clear out the labels in all apps/workspace.
             forceReload();
         } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
-                || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)
                 || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
-                || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
+                || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)
+                || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) {
             UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
             if (TestProtocol.sDebugTracing) {
                 Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action +
@@ -305,9 +314,7 @@
                             PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
                 }
 
-                // ACTION_PROFILE_INACCESSIBLE sends the profile back to locked mode, so
-                // we need to run the state change task again.
-                if (Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)
+                if (ACTION_PROFILE_LOCKED.equals(action)
                         || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
                     enqueueModelUpdateTask(new UserLockStateChangedTask(
                             user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)));
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8dc1204..f27ad7b 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -142,10 +142,6 @@
             "Enables haptics opening/closing All apps");
 
     // TODO(Block 6): Clean up flags
-    public static final BooleanFlag WIDGETS_IN_LAUNCHER_PREVIEW = getDebugFlag(270393268,
-            "WIDGETS_IN_LAUNCHER_PREVIEW", ENABLED,
-            "Enables widgets in Launcher preview for the Wallpaper app.");
-
     public static final BooleanFlag ENABLE_ALL_APPS_SEARCH_IN_TASKBAR = getDebugFlag(270393900,
             "ENABLE_ALL_APPS_SEARCH_IN_TASKBAR", DISABLED,
             "Enables Search box in Taskbar All Apps.");
@@ -211,17 +207,9 @@
             "Allows on device search in all apps logging");
 
     // TODO(Block 14): Cleanup flags
-    public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(270391641,
-            "ASSISTANT_GIVES_LAUNCHER_FOCUS", DISABLED,
-            "Allow Launcher to handle nav bar gestures while Assistant is running over it");
-
     public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag(270393108, "NOTIFY_CRASHES",
             DISABLED, "Sends a notification whenever launcher encounters an uncaught exception.");
 
-    public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(270395077,
-            "FORCE_PERSISTENT_TASKBAR", DISABLED, "Forces taskbar to be persistent, even in gesture"
-                    + " nav mode and when transient taskbar is enabled.");
-
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798,
             "ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar.");
 
@@ -394,7 +382,7 @@
             "Enable initiating split screen from workspace to workspace.");
 
     public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
-            "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
+            "ENABLE_TRACKPAD_GESTURE", DISABLED, "Enables trackpad gesture.");
 
     // TODO(Block 29): Clean up flags
     public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 2465745..406955c 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 
 /**
@@ -150,7 +151,7 @@
         mInvalidateDelegate = invalidateDelegate;
 
         TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
-        mDotColor = ta.getColor(R.styleable.FolderIconPreview_folderDotColor, 0);
+        mDotColor = Themes.getAttrColor(context, R.attr.notificationDotColor);
         mStrokeColor = ta.getColor(R.styleable.FolderIconPreview_folderIconBorderColor, 0);
         mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0);
         ta.recycle();
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index b438e86..47677ea 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -105,7 +105,6 @@
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 import com.android.launcher3.widget.LocalColorExtractor;
-import com.android.launcher3.widget.NavigableAppWidgetHostView;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 import com.android.launcher3.widget.util.WidgetSizes;
 
@@ -281,9 +280,7 @@
         } else {
             mWallpaperColorResources = null;
         }
-        mAppWidgetHost = FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()
-                ? new LauncherPreviewAppWidgetHost(context)
-                : null;
+        mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
     }
 
     /** Populate preview and render it. */
@@ -416,19 +413,8 @@
 
     private void inflateAndAddWidgets(
             LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
-        AppWidgetHostView view;
-        if (FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()) {
-            view = mAppWidgetHost.createView(mContext, info.appWidgetId, providerInfo);
-        } else {
-            view = new NavigableAppWidgetHostView(this) {
-                @Override
-                protected boolean shouldAllowDirectClick() {
-                    return false;
-                }
-            };
-            view.setAppWidget(-1, providerInfo);
-            view.updateAppWidget(null);
-        }
+        AppWidgetHostView view = mAppWidgetHost.createView(
+                mContext, info.appWidgetId, providerInfo);
 
         if (mWallpaperColorResources != null) {
             view.setColorResources(mWallpaperColorResources);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 050e88f..6a972eb 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -589,17 +589,17 @@
         float scaledDividerHeight = dividerHeight * scale;
 
         if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
-            if (splitInfo.appsStackedVertically) {
-                outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
-            } else {
+            if (dp.isLandscape) {
                 outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
+            } else {
+                outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
             }
         } else {
-            if (splitInfo.appsStackedVertically) {
-                outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
-            } else {
+            if (dp.isLandscape) {
                 outRect.left += Math.round(outRect.width()
                         * (topLeftTaskPercent + dividerBarPercent));
+            } else {
+                outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
             }
         }
     }
@@ -610,9 +610,9 @@
             DeviceProfile dp, boolean isRtl) {
         int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
         int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
-        int dividerBar = Math.round(splitBoundsConfig.appsStackedVertically
-                ? splitBoundsConfig.dividerHeightPercent * dp.availableHeightPx
-                : splitBoundsConfig.dividerWidthPercent * parentWidth);
+        float dividerScale = splitBoundsConfig.appsStackedVertically
+                ? splitBoundsConfig.dividerHeightPercent
+                : splitBoundsConfig.dividerWidthPercent;
         int primarySnapshotHeight;
         int primarySnapshotWidth;
         int secondarySnapshotHeight;
@@ -620,12 +620,13 @@
         float taskPercent = splitBoundsConfig.appsStackedVertically ?
                 splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
         if (dp.isLandscape) {
+            int scaledDividerBar = Math.round(parentWidth * dividerScale);
             primarySnapshotHeight = totalThumbnailHeight;
             primarySnapshotWidth = Math.round(parentWidth * taskPercent);
 
             secondarySnapshotHeight = totalThumbnailHeight;
-            secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar;
-            int translationX = primarySnapshotWidth + dividerBar;
+            secondarySnapshotWidth = parentWidth - primarySnapshotWidth - scaledDividerBar;
+            int translationX = primarySnapshotWidth + scaledDividerBar;
             if (isRtl) {
                 primarySnapshot.setTranslationX(-translationX);
                 secondarySnapshot.setTranslationX(0);
@@ -640,7 +641,7 @@
         } else {
             float scale = (float) totalThumbnailHeight / dp.availableHeightPx;
             float topTaskHeight = dp.availableHeightPx * taskPercent;
-            float finalDividerHeight = dividerBar * scale;
+            float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
             float scaledTopTaskHeight = topTaskHeight * scale;
             primarySnapshotWidth = parentWidth;
             primarySnapshotHeight = Math.round(scaledTopTaskHeight);
diff --git a/tests/Android.bp b/tests/Android.bp
index fa0cdf2..e7f4084 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -51,6 +51,7 @@
       "src/com/android/launcher3/util/WidgetUtils.java",
       "src/com/android/launcher3/util/rule/FailureWatcher.java",
       "src/com/android/launcher3/util/rule/LauncherActivityRule.java",
+      "src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
       "src/com/android/launcher3/util/rule/SamplerRule.java",
       "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
       "src/com/android/launcher3/util/rule/ShellCommandRule.java",
@@ -132,4 +133,4 @@
     manifest: "shared/AndroidManifest.xml",
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
- }
\ No newline at end of file
+ }
diff --git a/tests/src/com/android/launcher3/DeleteDropTargetTest.kt b/tests/src/com/android/launcher3/DeleteDropTargetTest.kt
index 84c617c..bcfb90b 100644
--- a/tests/src/com/android/launcher3/DeleteDropTargetTest.kt
+++ b/tests/src/com/android/launcher3/DeleteDropTargetTest.kt
@@ -8,7 +8,6 @@
 import com.android.launcher3.util.ActivityContextWrapper
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -26,19 +25,15 @@
         enableRunningInTestHarnessForTests()
     }
 
-    // Needs mText, mTempRect, getPaddingTop, getPaddingBottom
-    // availableHeight as a parameter
-    @Ignore("TODO(b/279464742)")
     @Test
     fun isTextClippedVerticallyTest() {
-        buttonDropTarget.mText = "My Test"
+        buttonDropTarget.updateText("My Test")
+        buttonDropTarget.setPadding(0, 0, 0, 0)
+        buttonDropTarget.setTextMultiLine(false)
 
         // No space for text
         assertThat(buttonDropTarget.isTextClippedVertically(30)).isTrue()
 
-        // Some space for text, and just enough that the text should not be clipped
-        assertThat(buttonDropTarget.isTextClippedVertically(50)).isFalse()
-
         // A lot of space for text so the text should not be clipped
         assertThat(buttonDropTarget.isTextClippedVertically(100)).isFalse()
     }
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index e3de500..ee05fe6 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -87,15 +87,13 @@
         // modify the device profile.
         dp.inv.numColumns = width;
         dp.inv.numRows = height;
+        dp.cellLayoutBorderSpacePx = new Point(0, 0);
 
         CellLayout cl = new CellLayout(getWrappedContext(c, dp));
         // I put a very large number for width and height so that all the items can fit, it doesn't
         // need to be exact, just bigger than the sum of cell border
         cl.measure(View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY),
                 View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY));
-
-        cl.measure(View.MeasureSpec.makeMeasureSpec(cl.getDesiredWidth(), View.MeasureSpec.EXACTLY),
-                View.MeasureSpec.makeMeasureSpec(cl.getDesiredHeight(), View.MeasureSpec.EXACTLY));
         return cl;
     }
 
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 3141c7b..d7c4ae3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -72,6 +72,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.ViewCaptureRule;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -215,14 +216,15 @@
     }
 
     protected TestRule getRulesInsideActivityMonitor() {
+        final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
         final RuleChain inner = RuleChain
                 .outerRule(new PortraitLandscapeRunner(this))
-                .around(new FailureWatcher(mDevice, mLauncher));
+                .around(viewCaptureRule)
+                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
 
         return TestHelpers.isInLauncherProcess()
-                ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
-                .around(inner) :
-                inner;
+                ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
+                : inner;
     }
 
     @Rule
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 51facf4..19e7b13 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -7,8 +7,12 @@
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.uiautomator.UiDevice;
 
+import com.android.app.viewcapture.ViewCapture;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 
@@ -29,10 +33,14 @@
     private static boolean sSavedBugreport = false;
     final private UiDevice mDevice;
     private final LauncherInstrumentation mLauncher;
+    @NonNull
+    private final ViewCapture mViewCapture;
 
-    public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
+    public FailureWatcher(UiDevice device, LauncherInstrumentation launcher,
+            @NonNull ViewCapture viewCapture) {
         mDevice = device;
         mLauncher = launcher;
+        mViewCapture = viewCapture;
     }
 
     @Override
@@ -82,7 +90,7 @@
 
     @Override
     protected void failed(Throwable e, Description description) {
-        onError(mLauncher, description, e);
+        onError(mLauncher, description, e, mViewCapture);
     }
 
     static File diagFile(Description description, String prefix, String ext) {
@@ -93,8 +101,12 @@
 
     public static void onError(LauncherInstrumentation launcher, Description description,
             Throwable e) {
-        final UiDevice device = launcher.getDevice();
-        if (device == null) return;
+        onError(launcher, description, e, null);
+    }
+
+    private static void onError(LauncherInstrumentation launcher, Description description,
+            Throwable e, @Nullable ViewCapture viewCapture) {
+
         final File sceenshot = diagFile(description, "TestScreenshot", "png");
         final File hierarchy = diagFile(description, "Hierarchy", "zip");
 
@@ -109,13 +121,20 @@
             out.putNextEntry(new ZipEntry("visible_windows.zip"));
             dumpCommand("cmd window dump-visible-window-views", out);
             out.closeEntry();
-        } catch (IOException ex) {
+
+            if (viewCapture != null) {
+                out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc"));
+                viewCapture.dumpTo(out, ApplicationProvider.getApplicationContext());
+                out.closeEntry();
+            }
+        } catch (Exception ignored) {
         }
 
         Log.e(TAG, "Failed test " + description.getMethodName()
                 + ",\nscreenshot will be saved to " + sceenshot
                 + ",\nUI dump at: " + hierarchy
                 + " (use go/web-hv to open the dump file)", e);
+        final UiDevice device = launcher.getDevice();
         device.takeScreenshot(sceenshot);
 
         // Dump accessibility hierarchy
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 2093682..e9a52f8 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -56,4 +56,4 @@
             return launcher.getWorkspace().getFirstMatch(op) != null;
         };
     }
-}
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
index 1dbba6a..2eedec3 100644
--- a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
@@ -102,4 +102,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
new file mode 100644
index 0000000..e4713b2
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
@@ -0,0 +1,77 @@
+/*
+ * 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 android.app.Activity
+import android.app.Application
+import android.media.permission.SafeCloseable
+import android.os.Bundle
+import androidx.test.core.app.ApplicationProvider
+import com.android.app.viewcapture.SimpleViewCapture
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * This JUnit TestRule registers a listener for activity lifecycle events to attach a ViewCapture
+ * instance that other test rules use to dump the timelapse hierarchy upon an error during a test.
+ *
+ * This rule will not work in OOP tests that don't have access to the activity under test.
+ */
+class ViewCaptureRule : TestRule {
+    val viewCapture = SimpleViewCapture("test-view-capture")
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return object : Statement() {
+            override fun evaluate() {
+                val windowListenerCloseables = mutableListOf<SafeCloseable>()
+
+                val lifecycleCallbacks =
+                    object : ActivityLifecycleCallbacksAdapter {
+                        override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
+                            super.onActivityCreated(activity, bundle)
+                            windowListenerCloseables.add(
+                                viewCapture.startCapture(
+                                    activity.window.decorView,
+                                    "${description.testClass?.simpleName}.${description.methodName}"
+                                )
+                            )
+                        }
+
+                        override fun onActivityDestroyed(activity: Activity) {
+                            super.onActivityDestroyed(activity)
+                            viewCapture.stopCapture(activity.window.decorView)
+                        }
+                    }
+
+                val application = ApplicationProvider.getApplicationContext<Application>()
+                application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
+
+                try {
+                    base.evaluate()
+                } finally {
+                    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
+
+                    // Clean up ViewCapture references here rather than in onActivityDestroyed so
+                    // test code can access view hierarchy capture. onActivityDestroyed would delete
+                    // view capture data before FailureWatcher could output it as a test artifact.
+                    windowListenerCloseables.onEach(SafeCloseable::close)
+                }
+            }
+        }
+    }
+}