Snap for 9690572 from a176ad060f03342dba36a12e76502b946b8854ed to tm-qpr3-release

Change-Id: If89be5adc47d7b536efb93ca9526b66cf2132c58
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 9f3be69..0dde1bd 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -118,7 +118,7 @@
     private void sendEvent(AppTarget target, LauncherAtom.ItemInfo locationInfo, int eventId,
             int targetPredictor) {
         // TODO: remove the running test check when b/231648228 is fixed.
-        if (target != null && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (target != null && !Utilities.isRunningInTestHarness()) {
             AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
                     .setLaunchLocation(getContainer(locationInfo))
                     .build();
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index b00c4cb..ac2c44b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -198,15 +198,7 @@
      */
     public Animator createAnimToLauncher(@NonNull LauncherState toState,
             @NonNull RecentsAnimationCallbacks callbacks, long duration) {
-        AnimatorSet set = new AnimatorSet();
-        Animator taskbarState = mTaskbarLauncherStateController
-                .createAnimToLauncher(toState, callbacks, duration);
-        long halfDuration = Math.round(duration * 0.5f);
-        Animator translation =
-                mControllers.taskbarTranslationController.createAnimToLauncher(halfDuration);
-
-        set.playTogether(taskbarState, translation);
-        return set;
+        return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
     }
 
     public boolean isDraggingItem() {
@@ -280,7 +272,7 @@
      * Returns {@code true} if a Taskbar education should be shown on application launch.
      */
     public boolean shouldShowEduOnAppLaunch() {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             return false;
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 2fa22f1..2864ac7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -24,7 +24,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-import static com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS;
+import static com.android.launcher3.Utilities.isRunningInTestHarness;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
@@ -337,8 +337,7 @@
         int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_SLIPPERY
                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-        if (DisplayController.isTransientTaskbar(this)
-                && !IS_RUNNING_IN_TEST_HARNESS) {
+        if (DisplayController.isTransientTaskbar(this) && !isRunningInTestHarness()) {
             windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 00dfaf2..bc582e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -28,7 +28,6 @@
 import com.airbnb.lottie.model.KeyPath
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
-import com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS
 import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_EDU_TOOLTIP
 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
@@ -56,7 +55,8 @@
 class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
     LoggableTaskbarController {
 
-    private val isTooltipEnabled = !IS_RUNNING_IN_TEST_HARNESS && ENABLE_TASKBAR_EDU_TOOLTIP.get()
+    private val isTooltipEnabled: Boolean
+        get() = !Utilities.isRunningInTestHarness() && ENABLE_TASKBAR_EDU_TOOLTIP.get()
     private val isOpen: Boolean
         get() = tooltip?.isOpen ?: false
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index b586487..5ac0570 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -433,6 +433,14 @@
             });
             animatorSet.play(stashAnimator);
         }
+
+        if (isAnimatingToLauncher() || mLauncherState == LauncherState.NORMAL) {
+            // Translate back to 0 at a shorter or same duration as the icon alignment animation.
+            // This ensures there is no jump after switching to hotseat, e.g. when swiping up from
+            // overview to home. Currently we do duration / 2 just to make it feel snappier.
+            animatorSet.play(mControllers.taskbarTranslationController
+                    .createAnimToResetTranslation(duration / 2));
+        }
     }
 
     private boolean isInLauncher() {
@@ -460,7 +468,7 @@
         updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0);
 
         // Sync the first frame where we swap taskbar and hotseat.
-        if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (firstFrameVisChanged && mCanSyncViews && !Utilities.isRunningInTestHarness()) {
             ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(),
                     mControllers.taskbarActivityContext.getDragLayer(),
                     () -> {});
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 115db25..babafd5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -297,7 +297,7 @@
         }
         return supportsVisualStashing()
                 && isInApp()
-                && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests)
+                && (!Utilities.isRunningInTestHarness() || mEnableManualStashingDuringTests)
                 && !DisplayController.isTransientTaskbar(mActivity);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index a6b2a8a..062b4ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -135,9 +135,9 @@
     }
 
     /**
-     * Returns an animation to reset the taskbar translation for animation back to launcher.
+     * Returns an animation to reset the taskbar translation to {@code 0}.
      */
-    public ObjectAnimator createAnimToLauncher(long duration) {
+    public ObjectAnimator createAnimToResetTranslation(long duration) {
         ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
         animator.setInterpolator(Interpolators.LINEAR);
         animator.setDuration(duration);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index ac92374..2f8cae8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.anim.AnimatedFloat.VALUE;
@@ -27,13 +28,15 @@
 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
 
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.graphics.Rect;
-import android.util.FloatProperty;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
@@ -43,10 +46,10 @@
 import androidx.core.graphics.ColorUtils;
 import androidx.core.view.OneShotPreDrawListener;
 
-import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
+import com.android.launcher3.Reorderable;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AlphaUpdateListener;
 import com.android.launcher3.anim.AnimatedFloat;
@@ -56,7 +59,6 @@
 import com.android.launcher3.anim.RevealOutlineAnimation;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.ThemedIconDrawable;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -65,6 +67,7 @@
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.MultiValueAlpha;
 
 import java.io.PrintWriter;
@@ -338,18 +341,34 @@
                 croppedTransX = newLeft - iconLeft;
             }
 
-            as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_X, isStashed
-                    ? new float[] {croppedTransX}
-                    : new float[] {croppedTransX, 0}));
-
             float croppedTransY = child.getHeight() - stashedBounds.height();
-            as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_Y, isStashed
-                    ? new float[] {croppedTransY}
-                    : new float[] {croppedTransY, 0}));
-            as.addListener(forEndCallback(() -> {
-                ICON_REVEAL_TRANSLATE_X.set(child, 0f);
-                ICON_REVEAL_TRANSLATE_Y.set(child, 0f);
-            }));
+            if (child instanceof Reorderable) {
+                MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
+
+                as.play(ObjectAnimator.ofFloat(mtd.getTranslationX(INDEX_TASKBAR_REVEAL_ANIM),
+                        MULTI_PROPERTY_VALUE, isStashed
+                                ? new float[] {croppedTransX}
+                                : new float[] {croppedTransX, 0}));
+                as.play(ObjectAnimator.ofFloat(mtd.getTranslationX(INDEX_TASKBAR_REVEAL_ANIM),
+                        MULTI_PROPERTY_VALUE, isStashed
+                                ? new float[] {croppedTransY}
+                                : new float[] {croppedTransY, 0}));
+                as.addListener(forEndCallback(() ->
+                        mtd.setTranslation(INDEX_TASKBAR_REVEAL_ANIM, 0, 0)));
+            } else {
+                as.play(ObjectAnimator.ofFloat(child,
+                        VIEW_TRANSLATE_X, isStashed
+                                ? new float[] {croppedTransX}
+                                : new float[] {croppedTransX, 0}));
+                as.play(ObjectAnimator.ofFloat(child,
+                        VIEW_TRANSLATE_Y, isStashed
+                                ? new float[] {croppedTransY}
+                                : new float[] {croppedTransY, 0}));
+                as.addListener(forEndCallback(() -> {
+                    child.setTranslationX(0);
+                    child.setTranslationY(0);
+                }));
+            }
         }
         return as;
     }
@@ -435,7 +454,7 @@
                 float childCenter = (child.getLeft() + child.getRight()) / 2f;
                 float halfQsbIconWidthDiff =
                         (launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f;
-                setter.addFloat(child, ICON_TRANSLATE_X,
+                setter.addFloat(child, VIEW_TRANSLATE_X,
                         isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
                         hotseatIconCenter - childCenter, interpolator);
 
@@ -479,10 +498,18 @@
                     + hotseatCellSize / 2f;
 
             float childCenter = (child.getLeft() + child.getRight()) / 2f;
-            setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
+            if (child instanceof Reorderable) {
+                MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
 
-            setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
-
+                setter.setFloat(mtd.getTranslationX(INDEX_TASKBAR_ALIGNMENT_ANIM),
+                        MULTI_PROPERTY_VALUE, hotseatIconCenter - childCenter, interpolator);
+                setter.setFloat(mtd.getTranslationX(INDEX_TASKBAR_ALIGNMENT_ANIM),
+                        MULTI_PROPERTY_VALUE, mTaskbarBottomMargin, interpolator);
+            } else {
+                setter.setFloat(child, VIEW_TRANSLATE_X,
+                        hotseatIconCenter - childCenter, interpolator);
+                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+            }
             setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
         }
 
@@ -667,107 +694,4 @@
             mControllers.uiController.onIconLayoutBoundsChanged();
         }
     }
-
-    public static final FloatProperty<View> ICON_TRANSLATE_X =
-            new FloatProperty<View>("taskbarAlignmentTranslateX") {
-
-                @Override
-                public void setValue(View view, float v) {
-                    if (view instanceof BubbleTextView) {
-                        ((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v);
-                    } else if (view instanceof FolderIcon) {
-                        ((FolderIcon) view).setTranslationXForTaskbarAlignmentAnimation(v);
-                    } else {
-                        view.setTranslationX(v);
-                    }
-                }
-
-                @Override
-                public Float get(View view) {
-                    if (view instanceof BubbleTextView) {
-                        return ((BubbleTextView) view)
-                                .getTranslationXForTaskbarAlignmentAnimation();
-                    } else if (view instanceof FolderIcon) {
-                        return ((FolderIcon) view).getTranslationXForTaskbarAlignmentAnimation();
-                    }
-                    return view.getTranslationX();
-                }
-            };
-
-    public static final FloatProperty<View> ICON_TRANSLATE_Y =
-            new FloatProperty<View>("taskbarAlignmentTranslateY") {
-
-                @Override
-                public void setValue(View view, float v) {
-                    if (view instanceof BubbleTextView) {
-                        ((BubbleTextView) view).setTranslationYForTaskbarAlignmentAnimation(v);
-                    } else if (view instanceof FolderIcon) {
-                        ((FolderIcon) view).setTranslationYForTaskbarAlignmentAnimation(v);
-                    } else {
-                        view.setTranslationY(v);
-                    }
-                }
-
-                @Override
-                public Float get(View view) {
-                    if (view instanceof BubbleTextView) {
-                        return ((BubbleTextView) view)
-                                .getTranslationYForTaskbarAlignmentAnimation();
-                    } else if (view instanceof FolderIcon) {
-                        return ((FolderIcon) view).getTranslationYForTaskbarAlignmentAnimation();
-                    }
-                    return view.getTranslationY();
-                }
-            };
-
-    public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_X =
-            new FloatProperty<View>("taskbarRevealTranslateX") {
-
-                @Override
-                public void setValue(View view, float v) {
-                    if (view instanceof BubbleTextView) {
-                        ((BubbleTextView) view).setTranslationXForTaskbarRevealAnimation(v);
-                    } else if (view instanceof FolderIcon) {
-                        ((FolderIcon) view).setTranslationXForTaskbarRevealAnimation(v);
-                    } else {
-                        view.setTranslationX(v);
-                    }
-                }
-
-                @Override
-                public Float get(View view) {
-                    if (view instanceof BubbleTextView) {
-                        return ((BubbleTextView) view).getTranslationXForTaskbarRevealAnimation();
-                    } else if (view instanceof FolderIcon) {
-                        return ((FolderIcon) view).getTranslationXForTaskbarRevealAnimation();
-                    }
-                    return view.getTranslationX();
-                }
-            };
-
-    public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_Y =
-            new FloatProperty<View>("taskbarRevealTranslateY") {
-
-                @Override
-                public void setValue(View view, float v) {
-                    if (view instanceof BubbleTextView) {
-                        ((BubbleTextView) view).setTranslationYForTaskbarRevealAnimation(v);
-                    } else if (view instanceof FolderIcon) {
-                        ((FolderIcon) view).setTranslationYForTaskbarRevealAnimation(v);
-                    } else {
-                        view.setTranslationY(v);
-                    }
-                }
-
-                @Override
-                public Float get(View view) {
-                    if (view instanceof BubbleTextView) {
-                        return ((BubbleTextView) view).getTranslationYForTaskbarRevealAnimation();
-                    } else if (view instanceof FolderIcon) {
-                        return ((FolderIcon) view).getTranslationYForTaskbarRevealAnimation();
-                    }
-                    return view.getTranslationY();
-                }
-            };
-
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 07fcf48..f16b43d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -91,7 +91,7 @@
             builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
             mRecentsView.updateEmptyMessage();
             // TODO(b/246283207): Remove logging once root cause of flake detected.
-            if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            if (Utilities.isRunningInTestHarness()) {
                 Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
                                 + mRecentsView.getCurrentPage()
                                 + ", getScrollForPage(getCurrentPage())): "
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 5eeeb36..a02f3de 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION;
 import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -119,6 +120,12 @@
                 long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
                         numPagesToScroll * PER_PAGE_SCROLL_DURATION);
                 config.duration = Math.max(config.duration, scrollDuration);
+
+                // Sync scroll so that it ends before or at the same time as the taskbar animation.
+                if (DisplayController.isTransientTaskbar(mActivity)
+                        && mActivity.getDeviceProfile().isTaskbarPresent) {
+                    config.duration = Math.min(config.duration, TASKBAR_TO_HOME_DURATION);
+                }
                 overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
             } else {
                 config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 0a155cb..4690d94 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -139,7 +139,7 @@
         if (IS_VERBOSE) {
             Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
         }
-        if (!Utilities.ATLEAST_R || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (!Utilities.ATLEAST_R || Utilities.isRunningInTestHarness()) {
             return;
         }
         SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
@@ -438,7 +438,7 @@
             }
 
             // TODO: remove this when b/231648228 is fixed.
-            if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            if (Utilities.isRunningInTestHarness()) {
                 return;
             }
             int cardinality = mCardinality.orElseGet(() -> getCardinality(atomInfo));
@@ -636,7 +636,7 @@
     }
 
     private static int getCardinality(LauncherAtom.ItemInfo info) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             return 0;
         }
         switch (info.getContainerInfo().getContainerCase()) {
@@ -758,7 +758,7 @@
     }
 
     private static int getHierarchy(LauncherAtom.ItemInfo info) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             return 0;
         }
         if (info.getContainerInfo().getContainerCase() == FOLDER) {
@@ -801,7 +801,7 @@
     }
 
     private static int getSearchAttributes(LauncherAtom.ItemInfo info) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             return 0;
         }
         ContainerInfo containerInfo = info.getContainerInfo();
diff --git a/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
index effdfdd..f6b2441 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java
@@ -15,12 +15,13 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_MOVE_FROM_CENTER_ANIM;
+
 import android.annotation.NonNull;
 import android.view.View;
 
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.widget.NavigableAppWidgetHostView;
+import com.android.launcher3.Reorderable;
+import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier;
 
 /**
@@ -31,12 +32,9 @@
 
     @Override
     public void apply(@NonNull View view, float x, float y) {
-        if (view instanceof NavigableAppWidgetHostView) {
-            ((NavigableAppWidgetHostView) view).setTranslationForMoveFromCenterAnimation(x, y);
-        } else if (view instanceof BubbleTextView) {
-            ((BubbleTextView) view).setTranslationForMoveFromCenterAnimation(x, y);
-        } else if (view instanceof FolderIcon) {
-            ((FolderIcon) view).setTranslationForMoveFromCenterAnimation(x, y);
+        if (view instanceof Reorderable) {
+            MultiTranslateDelegate mtd = ((Reorderable) view).getTranslateDelegate();
+            mtd.setTranslation(INDEX_MOVE_FROM_CENTER_ANIM, x, y);
         } else {
             view.setTranslationX(x);
             view.setTranslationY(y);
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 69ed2f8..4bc41bc 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -129,7 +129,7 @@
      * @param pointerIndex Index for the pointer being tracked in the motion event
      */
     public void addPosition(MotionEvent ev, int pointerIndex) {
-        long timeoutMs = Utilities.IS_RUNNING_IN_TEST_HARNESS
+        long timeoutMs = Utilities.isRunningInTestHarness()
                 ? TEST_HARNESS_TRIGGER_TIMEOUT
                 : mMakePauseHarderToTrigger
                         ? HARDER_TRIGGER_TIMEOUT
@@ -195,7 +195,7 @@
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
             String logString = "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason;
-            if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            if (Utilities.isRunningInTestHarness()) {
                 Log.d(TAG, logString);
             }
             ActiveGestureLog.INSTANCE.addLog(logString);
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index e928b27..cf07e6e 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -64,7 +64,7 @@
             });
         }
 
-        if (!Utilities.IS_RUNNING_IN_TEST_HARNESS
+        if (!Utilities.isRunningInTestHarness()
                 && !hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
             stateManager.addStateListener(new StateListener<LauncherState>() {
                 boolean mFromAllApps = false;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 16935c1..ac59403 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -147,6 +147,8 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.DynamicResource;
@@ -1718,7 +1720,7 @@
             int finalTargetPage = targetPage;
             runOnPageScrollsInitialized(() -> {
                 // TODO(b/246283207): Remove logging once root cause of flake detected.
-                if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+                if (Utilities.isRunningInTestHarness()) {
                     Log.d("b/246283207", "RecentsView#applyLoadPlan() -> "
                             + "previousCurrentPage: " + previousCurrentPage
                             + ", targetPage: " + finalTargetPage
@@ -4491,6 +4493,7 @@
      * Attempts to initiate split with an existing taskView, if one exists
      */
     public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "enterSplitSelect");
         mSplitSelectSource = splitSelectSource;
         mSplitHiddenTaskView = getTaskViewByTaskId(splitSelectSource.alreadyRunningTaskId);
         mSplitHiddenTaskViewIndex = indexOfChild(mSplitHiddenTaskView);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
new file mode 100644
index 0000000..f10b917
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.content.Intent;
+
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TaplTestsSplitscreen extends AbstractQuickStepTest {
+    private static final String CALCULATOR_APP_NAME = "Calculator";
+    private static final String CALCULATOR_APP_PACKAGE =
+            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        TaplTestsLauncher3.initialize(this);
+
+        mLauncher.getWorkspace()
+                .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
+                .switchToAllApps()
+                .getAppIcon(CALCULATOR_APP_NAME)
+                .dragToHotseat(0);
+
+        startAppFast(CALCULATOR_APP_PACKAGE);
+        mLauncher.enableBlockTimeout(true);
+        mLauncher.showTaskbarIfHidden();
+    }
+
+    @After
+    public void tearDown() {
+        mLauncher.enableBlockTimeout(false);
+    }
+
+    @Test
+    // TODO (b/270201357): When this test is proven stable, remove this TestStabilityRule and
+    // introduce into presubmit as well.
+    @TestStabilityRule.Stability(
+            flavors = TestStabilityRule.LOCAL | TestStabilityRule.PLATFORM_POSTSUBMIT)
+    @PortraitLandscape
+    @TaskbarModeSwitch
+    public void testSplitAppFromHomeWithItself() throws Exception {
+        Assume.assumeTrue(mLauncher.isTablet());
+
+        mLauncher.goHome()
+                .switchToAllApps()
+                .getAppIcon(CALCULATOR_APP_NAME)
+                .openMenu()
+                .getSplitScreenMenuItem()
+                .click();
+
+        mLauncher.getLaunchedAppState()
+                .getTaskbar()
+                .getAppIcon(CALCULATOR_APP_NAME)
+                .launchIntoSplitScreen();
+    }
+}
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index b02e3e3..455217f 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -23,19 +23,20 @@
         android:clipToPadding="false"
         android:layout_below="@id/collapse_handle"
         android:descendantFocusability="afterDescendants"
-        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
         launcher:pageIndicator="@+id/tabs" >
 
         <com.android.launcher3.widget.picker.WidgetsRecyclerView
             android:id="@+id/primary_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:clipToPadding="false" />
 
         <com.android.launcher3.widget.picker.WidgetsRecyclerView
             android:id="@+id/work_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:clipToPadding="false" />
 
     </com.android.launcher3.widget.picker.WidgetPagedView>
@@ -47,6 +48,7 @@
         android:layout_height="wrap_content"
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="0dp"
+        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
         android:clipToOutline="true"
         android:orientation="vertical">
 
@@ -57,7 +59,6 @@
             android:gravity="center_horizontal"
             android:textSize="24sp"
             android:layout_marginTop="24dp"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:textColor="?android:attr/textColorSecondary"
             android:text="@string/widget_button_text"/>
 
@@ -68,7 +69,6 @@
             android:elevation="0.1dp"
             android:background="?android:attr/colorBackground"
             android:paddingBottom="8dp"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             launcher:layout_sticky="true">
             <include layout="@layout/widgets_search_bar" />
         </FrameLayout>
@@ -80,7 +80,6 @@
             android:layout_marginTop="8dp"
             android:background="@drawable/widgets_surface_background"
             android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
-            android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
             android:visibility="gone" />
 
         <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
@@ -90,7 +89,6 @@
             android:gravity="center_horizontal"
             android:orientation="horizontal"
             android:paddingVertical="8dp"
-            android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
             android:background="?android:attr/colorBackground"
             style="@style/TextHeadline"
             launcher:layout_sticky="true">
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 366d2d2..887f00c 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -20,7 +20,7 @@
         android:layout_below="@id/collapse_handle"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
         android:clipToPadding="false" />
 
     <!-- SearchAndRecommendationsView without the tab layout as well -->
@@ -30,7 +30,7 @@
         android:layout_height="wrap_content"
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="16dp"
-        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
         android:clipToOutline="true"
         android:orientation="vertical">
 
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 94b8cd8..bc4a5c3 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -827,6 +827,6 @@
     private boolean hasSeenReconfigurableWidgetEducationTip() {
         return mLauncher.getSharedPrefs()
                 .getBoolean(KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN, false)
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+                || Utilities.isRunningInTestHarness();
     }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index df38c26..3eb03ed 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -34,7 +34,6 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -71,6 +70,7 @@
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.views.ActivityContext;
@@ -100,21 +100,8 @@
 
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
 
-    private final PointF mTranslationForReorderBounce = new PointF(0, 0);
-    private final PointF mTranslationForReorderPreview = new PointF(0, 0);
-
-    private float mTranslationXForTaskbarAlignmentAnimation = 0f;
-    private float mTranslationYForTaskbarAlignmentAnimation = 0f;
-
-    private float mTranslationXForTaskbarRevealAnimation = 0f;
-    private float mTranslationYForTaskbarRevealAnimation = 0f;
-
-    private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
-
     private float mScaleForReorderBounce = 1f;
 
-    private float mTranslationXForTaskbarAllAppsIcon = 0f;
-
     private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
             = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
         @Override
@@ -142,6 +129,7 @@
         }
     };
 
+    private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
     private final ActivityContext mActivity;
     private FastBitmapDrawable mIcon;
     private boolean mCenterVertically;
@@ -960,131 +948,23 @@
                 mDisplay == DISPLAY_SEARCH_RESULT_SMALL;
     }
 
-    private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x
-                + mTranslationForReorderPreview.x
-                + mTranslationXForTaskbarAllAppsIcon
-                + mTranslationForMoveFromCenterAnimation.x
-                + mTranslationXForTaskbarAlignmentAnimation
-                + mTranslationXForTaskbarRevealAnimation
-        );
-        super.setTranslationY(mTranslationForReorderBounce.y
-                + mTranslationForReorderPreview.y
-                + mTranslationForMoveFromCenterAnimation.y
-                + mTranslationYForTaskbarAlignmentAnimation
-                + mTranslationYForTaskbarRevealAnimation);
-    }
-
-    /**
-     * Sets translationX for taskbar all apps icon
-     */
-    public void setTranslationXForTaskbarAllAppsIcon(float translationX) {
-        mTranslationXForTaskbarAllAppsIcon = translationX;
-        updateTranslation();
-    }
-
-    public void setReorderBounceOffset(float x, float y) {
-        mTranslationForReorderBounce.set(x, y);
-        updateTranslation();
-    }
-
-    public void getReorderBounceOffset(PointF offset) {
-        offset.set(mTranslationForReorderBounce);
+    @Override
+    public MultiTranslateDelegate getTranslateDelegate() {
+        return mTranslateDelegate;
     }
 
     @Override
-    public void setReorderPreviewOffset(float x, float y) {
-        mTranslationForReorderPreview.set(x, y);
-        updateTranslation();
-    }
-
-    @Override
-    public void getReorderPreviewOffset(PointF offset) {
-        offset.set(mTranslationForReorderPreview);
-    }
-
     public void setReorderBounceScale(float scale) {
         mScaleForReorderBounce = scale;
         super.setScaleX(scale);
         super.setScaleY(scale);
     }
 
+    @Override
     public float getReorderBounceScale() {
         return mScaleForReorderBounce;
     }
 
-    /**
-     * Sets translation values for move from center animation
-     */
-    public void setTranslationForMoveFromCenterAnimation(float x, float y) {
-        mTranslationForMoveFromCenterAnimation.set(x, y);
-        updateTranslation();
-    }
-
-    /**
-     * Sets translationX for taskbar to launcher alignment animation
-     */
-    public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
-        mTranslationXForTaskbarAlignmentAnimation = translationX;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translationX value for taskbar to launcher alignment animation
-     */
-    public float getTranslationXForTaskbarAlignmentAnimation() {
-        return mTranslationXForTaskbarAlignmentAnimation;
-    }
-
-    /**
-     * Sets translationX for taskbar to launcher alignment animation
-     */
-    public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
-        mTranslationYForTaskbarAlignmentAnimation = translationY;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translationY value for taskbar to launcher alignment animation
-     */
-    public float getTranslationYForTaskbarAlignmentAnimation() {
-        return mTranslationYForTaskbarAlignmentAnimation;
-    }
-
-    /**
-     * Sets translationX value for taskbar reveal animation
-     */
-    public void setTranslationXForTaskbarRevealAnimation(float translationX) {
-        mTranslationXForTaskbarRevealAnimation = translationX;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translation values for taskbar reveal animation
-     */
-    public float getTranslationXForTaskbarRevealAnimation() {
-        return mTranslationXForTaskbarRevealAnimation;
-    }
-
-    /**
-     * Sets translationY value for taskbar reveal animation
-     */
-    public void setTranslationYForTaskbarRevealAnimation(float translationY) {
-        mTranslationYForTaskbarRevealAnimation = translationY;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translationY values for taskbar reveal animation
-     */
-    public float getTranslationYForTaskbarRevealAnimation() {
-        return mTranslationYForTaskbarRevealAnimation;
-    }
-
-    public View getView() {
-        return this;
-    }
-
     @Override
     public int getViewType() {
         return DRAGGABLE_ICON;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index b96e4df..731ad74 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -22,6 +22,8 @@
 import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
 import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_BOUNCE_OFFSET;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_PREVIEW_OFFSET;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -70,6 +72,7 @@
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.ParcelableSparseArray;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
@@ -1099,13 +1102,12 @@
             lp.isLockedToGrid = false;
             // End compute new x and y
 
-            item.getReorderPreviewOffset(mTmpPointF);
-            final float initPreviewOffsetX = mTmpPointF.x;
-            final float initPreviewOffsetY = mTmpPointF.y;
+            MultiTranslateDelegate mtd = item.getTranslateDelegate();
+            float initPreviewOffsetX = mtd.getTranslationX(INDEX_REORDER_PREVIEW_OFFSET).getValue();
+            float initPreviewOffsetY = mtd.getTranslationY(INDEX_REORDER_PREVIEW_OFFSET).getValue();
             final float finalPreviewOffsetX = newX - oldX;
             final float finalPreviewOffsetY = newY - oldY;
 
-
             // Exit early if we're not actually moving the view
             if (finalPreviewOffsetX == 0 && finalPreviewOffsetY == 0
                     && initPreviewOffsetX == 0 && initPreviewOffsetY == 0) {
@@ -1123,7 +1125,7 @@
                     float r = (Float) animation.getAnimatedValue();
                     float x = (1 - r) * initPreviewOffsetX + r * finalPreviewOffsetX;
                     float y = (1 - r) * initPreviewOffsetY + r * finalPreviewOffsetY;
-                    item.setReorderPreviewOffset(x, y);
+                    item.getTranslateDelegate().setTranslation(INDEX_REORDER_PREVIEW_OFFSET, x, y);
                 }
             });
             va.addListener(new AnimatorListenerAdapter() {
@@ -1134,7 +1136,8 @@
                     // place just yet.
                     if (!cancelled) {
                         lp.isLockedToGrid = true;
-                        item.setReorderPreviewOffset(0, 0);
+                        item.getTranslateDelegate()
+                                .setTranslation(INDEX_REORDER_PREVIEW_OFFSET, 0, 0);
                         child.requestLayout();
                     }
                     if (mReorderAnimators.containsKey(lp)) {
@@ -1434,7 +1437,7 @@
 
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
             if (c != null && !skip && (child instanceof Reorderable)) {
-                ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child,
+                ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child,
                         mode, lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY);
                 rha.animate();
             }
@@ -1456,8 +1459,8 @@
 
     // Class which represents the reorder preview animations. These animations show that an item is
     // in a temporary state, and hint at where the item will return to.
-    class ReorderPreviewAnimation {
-        final Reorderable child;
+    class ReorderPreviewAnimation<T extends View & Reorderable> {
+        final T child;
         float finalDeltaX;
         float finalDeltaY;
         float initDeltaX;
@@ -1477,7 +1480,7 @@
         float animationProgress = 0;
         ValueAnimator a;
 
-        public ReorderPreviewAnimation(Reorderable child, int mode, int cellX0, int cellY0,
+        ReorderPreviewAnimation(View childView, int mode, int cellX0, int cellY0,
                 int cellX1, int cellY1, int spanX, int spanY) {
             regionToCenterPoint(cellX0, cellY0, spanX, spanY, mTmpPoint);
             final int x0 = mTmpPoint[0];
@@ -1488,16 +1491,16 @@
             final int dX = x1 - x0;
             final int dY = y1 - y0;
 
-            this.child = child;
+            this.child = (T) childView;
             this.mode = mode;
             finalDeltaX = 0;
             finalDeltaY = 0;
 
-            child.getReorderBounceOffset(mTmpPointF);
-            initDeltaX = mTmpPointF.x;
-            initDeltaY = mTmpPointF.y;
+            MultiTranslateDelegate mtd = child.getTranslateDelegate();
+            initDeltaX = mtd.getTranslationX(INDEX_REORDER_BOUNCE_OFFSET).getValue();
+            initDeltaY = mtd.getTranslationY(INDEX_REORDER_BOUNCE_OFFSET).getValue();
             initScale = child.getReorderBounceScale();
-            finalScale = mChildScale - (CHILD_DIVIDEND / child.getView().getWidth()) * initScale;
+            finalScale = mChildScale - (CHILD_DIVIDEND / child.getWidth()) * initScale;
 
             int dir = mode == MODE_HINT ? -1 : 1;
             if (dX == dY && dX == 0) {
@@ -1573,7 +1576,7 @@
             float r1 = (mode == MODE_HINT && repeating) ? 1.0f : animationProgress;
             float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
             float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
-            child.setReorderBounceOffset(x, y);
+            child.getTranslateDelegate().setTranslation(INDEX_REORDER_BOUNCE_OFFSET, x, y);
             float s = animationProgress * finalScale + (1 - animationProgress) * initScale;
             child.setReorderBounceScale(s);
         }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index de60d05..22b07ef 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1647,7 +1647,7 @@
 
     @Override
     protected void onNewIntent(Intent intent) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Launcher.onNewIntent: " + intent);
         }
         Object traceToken = TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT);
@@ -3172,7 +3172,7 @@
 
                 // Setting the touch point to (-1, -1) will show the options popup in the center of
                 // the screen.
-                if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+                if (Utilities.isRunningInTestHarness()) {
                     Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up");
                 }
                 showDefaultOptions(-1, -1);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index c3d8a53..c7431ed 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -773,7 +773,7 @@
 
         if (mScroller.isFinished() && pageScrollChanged) {
             // TODO(b/246283207): Remove logging once root cause of flake detected.
-            if (Utilities.IS_RUNNING_IN_TEST_HARNESS && !(this instanceof Workspace)) {
+            if (Utilities.isRunningInTestHarness() && !(this instanceof Workspace)) {
                 Log.d("b/246283207", this.getClass().getSimpleName() + "#onLayout() -> "
                         + "if(mScroller.isFinished() && pageScrollChanged) -> getNextPage(): "
                         + getNextPage() + ", getScrollForPage(getNextPage()): "
@@ -1713,7 +1713,7 @@
             return false;
         }
 
-        if (FeatureFlags.IS_STUDIO_BUILD && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (FeatureFlags.IS_STUDIO_BUILD && !Utilities.isRunningInTestHarness()) {
             duration *= Settings.Global.getFloat(getContext().getContentResolver(),
                     Settings.Global.WINDOW_ANIMATION_SCALE, 1);
         }
diff --git a/src/com/android/launcher3/Reorderable.java b/src/com/android/launcher3/Reorderable.java
index 047fb01..5afd95d 100644
--- a/src/com/android/launcher3/Reorderable.java
+++ b/src/com/android/launcher3/Reorderable.java
@@ -16,33 +16,19 @@
 
 package com.android.launcher3;
 
-import android.graphics.PointF;
-import android.view.View;
+import com.android.launcher3.util.MultiTranslateDelegate;
 
 public interface Reorderable {
 
     /**
-     * Set the offset related to reorder hint and bounce animations
+     * Returns the delegate to control translation
      */
-    void setReorderBounceOffset(float x, float y);
-
-    void getReorderBounceOffset(PointF offset);
-
-    /**
-     * Set the offset related to previewing the new reordered position
-     */
-    void setReorderPreviewOffset(float x, float y);
-
-    void getReorderPreviewOffset(PointF offset);
+    MultiTranslateDelegate getTranslateDelegate();
 
     /**
      * Set the scale related to reorder hint and "bounce" animations
      */
     void setReorderBounceScale(float scale);
-    float getReorderBounceScale();
 
-    /**
-     * Get the com.android.view related to this object
-     */
-    View getView();
+    float getReorderBounceScale();
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 55b745b..b00199f 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.CellLayout.FOLDER;
 import static com.android.launcher3.CellLayout.HOTSEAT;
 import static com.android.launcher3.CellLayout.WORKSPACE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_WIDGET_CENTERING;
 
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -208,7 +209,8 @@
             float scaleY = appWidgetScale.y;
 
             nahv.setScaleToFit(Math.min(scaleX, scaleY));
-            nahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
+            nahv.getTranslateDelegate().setTranslation(INDEX_WIDGET_CENTERING,
+                    -(lp.width - (lp.width * scaleX)) / 2.0f,
                     -(lp.height - (lp.height * scaleY)) / 2.0f);
         }
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7d01f7b..59327dc 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -150,11 +150,14 @@
                         Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
     }
 
-    public static boolean IS_RUNNING_IN_TEST_HARNESS =
-                    ActivityManager.isRunningInTestHarness();
+    private static boolean sIsRunningInTestHarness = ActivityManager.isRunningInTestHarness();
+
+    public static boolean isRunningInTestHarness() {
+        return sIsRunningInTestHarness;
+    }
 
     public static void enableRunningInTestHarnessForTests() {
-        IS_RUNNING_IN_TEST_HARNESS = true;
+        sIsRunningInTestHarness = true;
     }
 
     public static boolean isPropertyEnabled(String propertyName) {
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 0188a47..df22425 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -127,7 +127,7 @@
                 || onboardingPrefs.getBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN)
                 || AbstractFloatingView.getTopOpenView(launcher) != null
                 || launcher.getSystemService(UserManager.class).isDemoUser()
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+                || Utilities.isRunningInTestHarness()) {
             return;
         }
 
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 770e931..24cc0ac 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -127,7 +127,7 @@
      */
     private static AccessibilityManager getAccessibilityManagerForTest(Context context) {
         // If not running in a test harness, don't participate in test exchanges.
-        if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return null;
+        if (!Utilities.isRunningInTestHarness()) return null;
 
         final AccessibilityManager accessibilityManager = getManager(context);
         if (!accessibilityManager.isEnabled()) return null;
diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 08e50dd..0e76bbb 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -43,7 +43,7 @@
 
     private long getEnterSpringLoadHoverTime() {
         // Some TAPL tests are flaky on Cuttlefish with a low waiting time
-        return Utilities.IS_RUNNING_IN_TEST_HARNESS
+        return Utilities.isRunningInTestHarness()
                 ? ENTER_SPRING_LOAD_HOVER_TIME_IN_TEST
                 : ENTER_SPRING_LOAD_HOVER_TIME;
     }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index ee1a060..86f4beb 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -28,7 +28,6 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
@@ -77,6 +76,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.IconLabelDotView;
@@ -93,6 +93,7 @@
 public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView,
         DraggableView, Reorderable {
 
+    private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
     @Thunk ActivityContext mActivity;
     @Thunk Folder mFolder;
     public FolderInfo mInfo;
@@ -133,14 +134,6 @@
 
     private Rect mTouchArea = new Rect();
 
-    private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
-    private float mTranslationXForTaskbarAlignmentAnimation = 0f;
-    private float mTranslationYForTaskbarAlignmentAnimation = 0f;
-    private float mTranslationXForTaskbarRevealAnimation = 0f;
-    private float mTranslationYForTaskbarRevealAnimation = 0f;
-
-    private final PointF mTranslationForReorderBounce = new PointF(0, 0);
-    private final PointF mTranslationForReorderPreview = new PointF(0, 0);
     private float mScaleForReorderBounce = 1f;
 
     private static final Property<FolderIcon, Float> DOT_SCALE_PROPERTY
@@ -770,120 +763,23 @@
         mPreviewItemManager.onFolderClose(currentPage);
     }
 
-    private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x
-                + mTranslationForReorderPreview.x
-                + mTranslationForMoveFromCenterAnimation.x
-                + mTranslationXForTaskbarAlignmentAnimation
-                + mTranslationXForTaskbarRevealAnimation);
-        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
-                + mTranslationForMoveFromCenterAnimation.y
-                + mTranslationYForTaskbarAlignmentAnimation
-                + mTranslationYForTaskbarRevealAnimation);
-    }
-
-    public void setReorderBounceOffset(float x, float y) {
-        mTranslationForReorderBounce.set(x, y);
-        updateTranslation();
-    }
-
-    public void getReorderBounceOffset(PointF offset) {
-        offset.set(mTranslationForReorderBounce);
-    }
-
-    /**
-     * Sets translationX value for taskbar to launcher alignment animation
-     */
-    public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
-        mTranslationXForTaskbarAlignmentAnimation = translationX;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translation values for taskbar to launcher alignment animation
-     */
-    public float getTranslationXForTaskbarAlignmentAnimation() {
-        return mTranslationXForTaskbarAlignmentAnimation;
-    }
-
-    /**
-     * Sets translationY value for taskbar to launcher alignment animation
-     */
-    public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
-        mTranslationYForTaskbarAlignmentAnimation = translationY;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translation values for taskbar to launcher alignment animation
-     */
-    public float getTranslationYForTaskbarAlignmentAnimation() {
-        return mTranslationYForTaskbarAlignmentAnimation;
-    }
-
-    /**
-     * Sets translationX value for taskbar reveal animation
-     */
-    public void setTranslationXForTaskbarRevealAnimation(float translationX) {
-        mTranslationXForTaskbarRevealAnimation = translationX;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translation values for taskbar reveal animation
-     */
-    public float getTranslationXForTaskbarRevealAnimation() {
-        return mTranslationXForTaskbarRevealAnimation;
-    }
-
-    /**
-     * Sets translationY value for taskbar reveal animation
-     */
-    public void setTranslationYForTaskbarRevealAnimation(float translationY) {
-        mTranslationYForTaskbarRevealAnimation = translationY;
-        updateTranslation();
-    }
-
-    /**
-     * Returns translationY values for taskbar reveal animation
-     */
-    public float getTranslationYForTaskbarRevealAnimation() {
-        return mTranslationYForTaskbarRevealAnimation;
-    }
-
-    /**
-     * Sets translation values for move from center animation
-     */
-    public void setTranslationForMoveFromCenterAnimation(float x, float y) {
-        mTranslationForMoveFromCenterAnimation.set(x, y);
-        updateTranslation();
+    @Override
+    public MultiTranslateDelegate getTranslateDelegate() {
+        return mTranslateDelegate;
     }
 
     @Override
-    public void setReorderPreviewOffset(float x, float y) {
-        mTranslationForReorderPreview.set(x, y);
-        updateTranslation();
-    }
-
-    @Override
-    public void getReorderPreviewOffset(PointF offset) {
-        offset.set(mTranslationForReorderPreview);
-    }
-
     public void setReorderBounceScale(float scale) {
         mScaleForReorderBounce = scale;
         super.setScaleX(scale);
         super.setScaleY(scale);
     }
 
+    @Override
     public float getReorderBounceScale() {
         return mScaleForReorderBounce;
     }
 
-    public View getView() {
-        return this;
-    }
-
     @Override
     public int getViewType() {
         return DRAGGABLE_ICON;
diff --git a/src/com/android/launcher3/testing/TestInformationProvider.java b/src/com/android/launcher3/testing/TestInformationProvider.java
index 5444d92..17b472a 100644
--- a/src/com/android/launcher3/testing/TestInformationProvider.java
+++ b/src/com/android/launcher3/testing/TestInformationProvider.java
@@ -61,7 +61,7 @@
 
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             TestInformationHandler handler = TestInformationHandler.newInstance(getContext());
             handler.init(getContext());
 
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index c151606..f95548d 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -39,13 +39,13 @@
     }
 
     public static void recordEvent(String sequence, String event) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             recordEventSlow(sequence, event);
         }
     }
 
     public static void recordEvent(String sequence, String message, Object parameter) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             recordEventSlow(sequence, message + ": " + parameter);
         }
     }
@@ -58,14 +58,14 @@
     }
 
     public static void recordKeyEvent(String sequence, String message, KeyEvent event) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+        if (Utilities.isRunningInTestHarness()) {
             recordEventSlow(sequence, message + ": " + event);
             registerEventNotFromTest(event);
         }
     }
 
     public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS && event.getAction() != MotionEvent.ACTION_MOVE) {
+        if (Utilities.isRunningInTestHarness() && event.getAction() != MotionEvent.ACTION_MOVE) {
             recordEventSlow(sequence, message + ": " + event);
             registerEventNotFromTest(event);
         }
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 9ed6700..3d455d8 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -139,11 +139,11 @@
         // TODO(b/258604917): When running in test harness, use !sTransientTaskbarStatusForTests
         //  once tests are updated to expect new persistent behavior such as not allowing long press
         //  to stash.
-        if (!Utilities.IS_RUNNING_IN_TEST_HARNESS && FORCE_PERSISTENT_TASKBAR.get()) {
+        if (!Utilities.isRunningInTestHarness() && FORCE_PERSISTENT_TASKBAR.get()) {
             return false;
         }
         return getInfo().navigationMode == NavigationMode.NO_BUTTON
-                && (Utilities.IS_RUNNING_IN_TEST_HARNESS
+                && (Utilities.isRunningInTestHarness()
                     ? sTransientTaskbarStatusForTests
                     : ENABLE_TRANSIENT_TASKBAR.get());
     }
diff --git a/src/com/android/launcher3/util/MultiTranslateDelegate.java b/src/com/android/launcher3/util/MultiTranslateDelegate.java
new file mode 100644
index 0000000..0b5bc8d
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiTranslateDelegate.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+
+import android.view.View;
+
+import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+
+/**
+ * A utility class to split translation components for various workspace items
+ */
+public class MultiTranslateDelegate {
+
+    // offset related to reorder hint and bounce animations
+    public static final int INDEX_REORDER_BOUNCE_OFFSET = 0;
+    // offset related to previewing the new reordered position
+    public static final int INDEX_REORDER_PREVIEW_OFFSET = 1;
+    public static final int INDEX_MOVE_FROM_CENTER_ANIM = 2;
+
+    // Specific for icons and folders
+    public static final int INDEX_TASKBAR_ALIGNMENT_ANIM = 3;
+    public static final int INDEX_TASKBAR_REVEAL_ANIM = 4;
+
+    // Specific for widgets
+    public static final int INDEX_WIDGET_CENTERING = 3;
+
+    public static final int COUNT = 5;
+
+    private final MultiPropertyFactory<View> mTranslationX;
+    private final MultiPropertyFactory<View> mTranslationY;
+
+    public MultiTranslateDelegate(View target) {
+        this(target, COUNT, COUNT);
+    }
+
+    public MultiTranslateDelegate(View target, int countX, int countY) {
+        mTranslationX = new MultiPropertyFactory<>(target, VIEW_TRANSLATE_X, countX, Float::sum);
+        mTranslationY = new MultiPropertyFactory<>(target, VIEW_TRANSLATE_Y, countY, Float::sum);
+    }
+
+    /**
+     * Helper method to set both translations, x and y at a given index
+     */
+    public void setTranslation(int index, float x, float y) {
+        getTranslationX(index).setValue(x);
+        getTranslationY(index).setValue(y);
+    }
+
+    /**
+     * Returns the translation x for the provided index
+     */
+    public MultiProperty getTranslationX(int index) {
+        return mTranslationX.get(index);
+    }
+
+    /**
+     * Returns the translation y for the provided index
+     */
+    public MultiProperty getTranslationY(int index) {
+        return mTranslationY.get(index);
+    }
+}
diff --git a/src/com/android/launcher3/views/IconButtonView.java b/src/com/android/launcher3/views/IconButtonView.java
index 9969eeb..71f6756 100644
--- a/src/com/android/launcher3/views/IconButtonView.java
+++ b/src/com/android/launcher3/views/IconButtonView.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.util.MultiTranslateDelegate;
 
 /**
  * Button in Taskbar that shows a tinted background and foreground.
@@ -45,6 +46,12 @@
 
     private static final int[] ATTRS = {android.R.attr.icon};
 
+    private static final int INDEX_TASKBAR_ALL_APPS_ICON = MultiTranslateDelegate.COUNT;
+    private static final int MY_COUNT = MultiTranslateDelegate.COUNT + 1;
+
+    private final MultiTranslateDelegate mTranslateDelegate =
+            new MultiTranslateDelegate(this, MY_COUNT, MultiTranslateDelegate.COUNT);
+
     public IconButtonView(Context context) {
         this(context, null);
     }
@@ -88,6 +95,18 @@
         }
     }
 
+    @Override
+    public MultiTranslateDelegate getTranslateDelegate() {
+        return mTranslateDelegate;
+    }
+
+    /**
+     * Sets translationX for taskbar all apps icon
+     */
+    public void setTranslationXForTaskbarAllAppsIcon(float translationX) {
+        getTranslateDelegate().getTranslationX(INDEX_TASKBAR_ALL_APPS_ICON).setValue(translationX);
+    }
+
     private static class IconDrawable extends FastBitmapDrawable {
 
         private final Drawable mFg;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 2ac1e94..68ece03 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -341,7 +341,7 @@
     /** Returns {@code true} if tip has previously been shown on any of {@link BaseWidgetSheet}. */
     protected boolean hasSeenEducationTip() {
         return mActivityContext.getSharedPrefs().getBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, false)
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+                || Utilities.isRunningInTestHarness();
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index 241c937..3389fb1 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -19,7 +19,6 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.view.KeyEvent;
 import android.view.View;
@@ -29,6 +28,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
@@ -39,20 +39,13 @@
 public abstract class NavigableAppWidgetHostView extends AppWidgetHostView
         implements DraggableView, Reorderable {
 
+    private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
+
     /**
      * The scaleX and scaleY value such that the widget fits within its cellspans, scaleX = scaleY.
      */
     private float mScaleToFit = 1f;
 
-    /**
-     * The translation values to center the widget within its cellspans.
-     */
-    private final PointF mTranslationForCentering = new PointF(0, 0);
-
-    private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
-
-    private final PointF mTranslationForReorderBounce = new PointF(0, 0);
-    private final PointF mTranslationForReorderPreview = new PointF(0, 0);
     private float mScaleForReorderBounce = 1f;
 
     private final Rect mTempRect = new Rect();
@@ -163,57 +156,23 @@
         setSelected(childIsFocused);
     }
 
-    public View getView() {
-        return this;
-    }
-
-    private void updateTranslation() {
-        super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
-                + mTranslationForCentering.x + mTranslationForMoveFromCenterAnimation.x);
-        super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
-                + mTranslationForCentering.y + mTranslationForMoveFromCenterAnimation.y);
-    }
-
-    public void setTranslationForCentering(float x, float y) {
-        mTranslationForCentering.set(x, y);
-        updateTranslation();
-    }
-
-    public void setTranslationForMoveFromCenterAnimation(float x, float y) {
-        mTranslationForMoveFromCenterAnimation.set(x, y);
-        updateTranslation();
-    }
-
-    public void setReorderBounceOffset(float x, float y) {
-        mTranslationForReorderBounce.set(x, y);
-        updateTranslation();
-    }
-
-    public void getReorderBounceOffset(PointF offset) {
-        offset.set(mTranslationForReorderBounce);
-    }
-
-    @Override
-    public void setReorderPreviewOffset(float x, float y) {
-        mTranslationForReorderPreview.set(x, y);
-        updateTranslation();
-    }
-
-    @Override
-    public void getReorderPreviewOffset(PointF offset) {
-        offset.set(mTranslationForReorderPreview);
-    }
-
     private void updateScale() {
         super.setScaleX(mScaleToFit * mScaleForReorderBounce);
         super.setScaleY(mScaleToFit * mScaleForReorderBounce);
     }
 
+    @Override
+    public MultiTranslateDelegate getTranslateDelegate() {
+        return mTranslateDelegate;
+    }
+
+    @Override
     public void setReorderBounceScale(float scale) {
         mScaleForReorderBounce = scale;
         updateScale();
     }
 
+    @Override
     public float getReorderBounceScale() {
         return mScaleForReorderBounce;
     }
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index ce47d70..80bc1a7 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -22,6 +22,7 @@
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.Utilities.ATLEAST_S;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_WIDGET_CENTERING;
 
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -314,7 +315,7 @@
                 setScaleToFit(1.0f);
             }
             // When the drag start, translations need to be set to zero to center the view
-            setTranslationForCentering(0f, 0f);
+            getTranslateDelegate().setTranslation(INDEX_WIDGET_CENTERING, 0f, 0f);
         }
     }
 
@@ -464,7 +465,8 @@
             } else {
                 mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
             }
-            mAppWidgetHostViewPreview.setTranslationForCentering(
+            mAppWidgetHostViewPreview.getTranslateDelegate().setTranslation(
+                    INDEX_WIDGET_CENTERING,
                     -(params.width - (params.width * mPreviewContainerScale)) / 2.0f,
                     -(params.height - (params.height * mPreviewContainerScale)) / 2.0f);
             mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 77781bd..f4d6749 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -939,7 +939,7 @@
     protected boolean hasSeenEducationDialog() {
         return mActivityContext.getSharedPrefs()
                 .getBoolean(KEY_WIDGETS_EDUCATION_DIALOG_SEEN, false)
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+                || Utilities.isRunningInTestHarness();
     }
 
     private void setUpEducationViewsIfNeeded() {
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
index 82d9630..667290f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
@@ -54,5 +54,14 @@
         return createMenuItem(menuItem);
     }
 
+    /**
+     * Returns a menu item that matches the text "Split screen". Fails if it doesn't exist.
+     */
+    public SplitScreenMenuItem getSplitScreenMenuItem() {
+        final UiObject2 menuItem = mLauncher.waitForObjectInContainer(mDeepShortcutsContainer,
+                AppIcon.getAppIconSelector("Split screen", mLauncher));
+        return new SplitScreenMenuItem(mLauncher, menuItem);
+    }
+
     protected abstract AppIconMenuItem createMenuItem(UiObject2 menuItem);
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 3dcb437..48e327f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -76,6 +76,27 @@
         }
     }
 
+    /**
+     * Clicks a launcher object to initiate splitscreen, where the selected app will be one of two
+     * apps running on the screen. Should be called when Launcher is in a "split staging" state
+     * and is waiting for the user's selection of a second app. Expects a SPLIT_START_EVENT to be
+     * fired when the click is executed.
+     */
+    public LaunchedAppState launchIntoSplitScreen() {
+        try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                "want to launch split tasks from " + launchableType())) {
+            LauncherInstrumentation.log("Launchable.launch before click "
+                    + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+
+            mLauncher.clickLauncherObject(mObject);
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_START_EVENT);
+                return new LaunchedAppState(mLauncher);
+            }
+        }
+    }
+
     protected LaunchedAppState assertAppLaunched(BySelector selector) {
         mLauncher.assertTrue(
                 "App didn't start: (" + selector + ")",
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index adc993d..90f3d13 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -37,10 +37,9 @@
 public final class OverviewTask {
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
 
-    static final Pattern TASK_START_EVENT =
-            Pattern.compile("startActivityFromRecentsAsync");
-    static final Pattern SPLIT_START_EVENT =
-            Pattern.compile("launchSplitTasks");
+    static final Pattern TASK_START_EVENT = Pattern.compile("startActivityFromRecentsAsync");
+    static final Pattern SPLIT_SELECT_EVENT = Pattern.compile("enterSplitSelect");
+    static final Pattern SPLIT_START_EVENT = Pattern.compile("launchSplitTasks");
     private final LauncherInstrumentation mLauncher;
     private final UiObject2 mTask;
     private final BaseOverview mOverview;
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitScreenMenuItem.java b/tests/tapl/com/android/launcher3/tapl/SplitScreenMenuItem.java
new file mode 100644
index 0000000..47cf20b
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SplitScreenMenuItem.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * A class representing the "Split screen" menu item in the app long-press menu. Used for TAPL
+ * testing in a similar way as other menu items {@link AppIconMenuItem}, but unlike AppIconMenuItem,
+ * the split screen command does not trigger an app launch. Instead, it causes Launcher to shift to
+ * a different state (OverviewSplitSelect).
+ */
+public final class SplitScreenMenuItem {
+    private final LauncherInstrumentation mLauncher;
+    private final UiObject2 mObject;
+
+    SplitScreenMenuItem(LauncherInstrumentation launcher, UiObject2 object) {
+        mLauncher = launcher;
+        mObject = object;
+    }
+
+    /**
+     * Executes a click command on this menu item. Expects a SPLIT_SELECT_EVENT to be fired.
+     */
+    public void click() {
+        try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                "want to enter split select from app long-press menu")) {
+            LauncherInstrumentation.log("clicking on split screen menu item "
+                    + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+
+            mLauncher.clickLauncherObject(mObject);
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_SELECT_EVENT);
+                mLauncher.waitForLauncherObject("split_placeholder");
+            }
+        }
+    }
+}