Merge "Init with mIsLumaSamplingEnabled = true" into main
diff --git a/Android.bp b/Android.bp
index 39b1ae0..eca0778 100644
--- a/Android.bp
+++ b/Android.bp
@@ -309,6 +309,8 @@
         "AndroidManifest-common.xml",
     ],
     min_sdk_version: "current",
+    // TODO(b/319712088): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 // Build rule for Quickstep library
@@ -337,6 +339,8 @@
     manifest: "quickstep/AndroidManifest.xml",
     platform_apis: true,
     min_sdk_version: "current",
+    // TODO(b/319712088): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 // Build rule for Launcher3 Go app for Android Go devices.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4f580e0..517bd6d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3">
-    <uses-sdk android:targetSdkVersion="33" android:minSdkVersion="26"/>
+    <uses-sdk android:targetSdkVersion="33" android:minSdkVersion="30"/>
     <!--
     Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
     Refer comments around specific entries on how to extend individual components.
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index 97e56b7..b846604 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -26,4 +26,11 @@
     namespace: "launcher_search"
     description: "This flag enables addition of App Installer button in Private Space container."
     bug: "308064949"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "private_space_restrict_accessibility_drag"
+    namespace: "launcher_search"
+    description: "This flag disables accessibility drag for Private Space Apps."
+    bug: "289223923"
+}
diff --git a/build.gradle b/build.gradle
index f4d7261..a453bfd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,8 +20,8 @@
     buildToolsVersion BUILD_TOOLS_VERSION
 
     defaultConfig {
-        minSdkVersion 26
-        targetSdkVersion 30
+        minSdkVersion 30
+        targetSdkVersion 33
         versionCode 1
         versionName "1.0"
 
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 9aab0a1..b23c283 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -230,6 +230,7 @@
     <string name="action_split">Split</string>
     <!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
     <string name="toast_split_select_app">Tap another app to use split screen</string>
+    <string name="toast_contextual_split_select_app">Choose another app to use split screen</string>
     <string name="toast_split_select_app_cancel"><b>Cancel</b></string>
     <string name="toast_split_select_cont_desc">Exit split screen selection</string>
     <!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 5d4e19d..842f0ef 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -24,9 +24,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
 import android.content.Context;
-import android.os.Build;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -57,7 +55,6 @@
  * the runner implementation.  When this animation manager is destroyed, we remove the Launcher
  * reference to the runner, leaving only the weak ref from the runner.
  */
-@TargetApi(Build.VERSION_CODES.P)
 public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat {
 
     private static final RemoteAnimationFactory DEFAULT_FACTORY =
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index f64b5cf..523923d 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -15,14 +15,10 @@
  */
 package com.android.launcher3;
 
-import android.annotation.TargetApi;
-import android.os.Build;
-
 import com.android.quickstep.util.ActivityInitListener;
 
 import java.util.function.BiPredicate;
 
-@TargetApi(Build.VERSION_CODES.P)
 public class LauncherInitListener extends ActivityInitListener<Launcher> {
 
     /**
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 656af31..e8a3d3e 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -64,6 +64,7 @@
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
+import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
 
@@ -88,6 +89,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -351,6 +353,9 @@
                 new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
                 new RemoteTransition(runner.toRemoteTransition(),
                         mLauncher.getIApplicationThread(), "QuickstepLaunch"));
+        IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback);
+        options.setOnAnimationAbortListener(endCallback);
+        options.setOnAnimationFinishedListener(endCallback);
         return new ActivityOptionsWrapper(options, onEndCallback);
     }
 
@@ -1663,14 +1668,20 @@
                     ? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK
                     : Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
 
-            anim.addListener(new AnimatorListenerAdapter() {
+            AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     super.onAnimationEnd(animation);
                     AccessibilityManagerCompat.sendTestProtocolEventToTest(
                             mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
                 }
-            });
+            };
+
+            if (fromPredictiveBack) {
+                rectFSpringAnim.addAnimatorListener(endListener);
+            } else {
+                anim.addListener(endListener);
+            }
 
             // Only register the content animation for cancellation when state changes
             mLauncher.getStateManager().setCurrentAnimation(anim);
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index e680ea9..caf8a0b 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -16,10 +16,8 @@
 
 package com.android.launcher3.appprediction;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -50,7 +48,6 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-@TargetApi(Build.VERSION_CODES.P)
 public class PredictionRowView<T extends Context & ActivityContext>
         extends LinearLayout implements OnDeviceProfileChangeListener, FloatingHeaderRow {
 
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index e17fc88..a931f36 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -45,14 +45,12 @@
 import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
-import android.annotation.TargetApi;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
 import android.app.prediction.AppTargetId;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
@@ -84,7 +82,6 @@
 /**
  * Utility class to track stats log and emit corresponding app events
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class AppEventProducer implements StatsLogConsumer {
 
     private static final int MSG_LAUNCH = 0;
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 667f784..0ce1cb8 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -68,6 +68,7 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntSparseArrayMap;
@@ -553,7 +554,10 @@
                     if (lai == null) {
                         return null;
                     }
-                    AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user));
+                    AppInfo info = new AppInfo(
+                            lai,
+                            UserCache.INSTANCE.get(mAppState.getContext()).getUserInfo(user),
+                            mUMS.isUserQuiet(user));
                     info.container = mContainer;
                     mAppState.getIconCache().getTitleAndIcon(info, lai, false);
                     mReadCount++;
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 003c2fc..3d04cb6 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -20,7 +20,6 @@
 
 import static com.android.launcher3.util.SimpleBroadcastReceiver.getPackageFilter;
 
-import android.annotation.TargetApi;
 import android.app.RemoteAction;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -30,7 +29,6 @@
 import android.content.pm.LauncherApps;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
@@ -63,7 +61,6 @@
 /**
  * Data model for digital wellbeing status of apps.
  */
-@TargetApi(Build.VERSION_CODES.Q)
 public final class WellbeingModel extends BgObjectWithLooper {
     private static final String TAG = "WellbeingModel";
     private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index 535c8ec..f981610 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -105,13 +105,6 @@
     }
 
     @Override
-    protected boolean isInOverview() {
-        TopTaskTracker.CachedTaskInfo topTask = TopTaskTracker.INSTANCE
-                .get(mControllers.taskbarActivityContext).getCachedTopTask(true);
-        return topTask.isRecentsTask();
-    }
-
-    @Override
     public RecentsView getRecentsView() {
         return mRecentsActivity.getOverviewPanel();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 20e977b..e1b6494 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
+import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
 import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
 import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
 import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
@@ -214,7 +215,14 @@
         }
 
         mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, isVisible);
-        return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
+        // TODO(b/308851855): Skip animation for launching split from home, will refine later
+        boolean skipAnimForSplit = enableSplitContextually() &&
+                mLauncher.areBothSplitAppsConfirmed() &&
+                mLauncher.getStateManager().getState() == LauncherState.NORMAL;
+        if (skipAnimForSplit || fromInit) {
+            duration = 0;
+        }
+        return mTaskbarLauncherStateController.applyState(duration, startAnimation);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 2b88f02..3f5793f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -37,24 +37,23 @@
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
+import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo.Config;
 import android.content.pm.LauncherApps;
 import android.content.res.Resources;
-import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
+import android.os.IRemoteCallback;
 import android.os.Process;
 import android.os.Trace;
 import android.provider.Settings;
@@ -66,7 +65,6 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManager;
-import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -134,13 +132,10 @@
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -744,13 +739,14 @@
     @Override
     public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
         RunnableList callbacks = new RunnableList();
-        ActivityOptions options = ActivityOptions.makeCustomAnimation(
-                this, 0, 0, Color.TRANSPARENT,
-                Executors.MAIN_EXECUTOR.getHandler(), null,
-                elapsedRealTime -> callbacks.executeAllAndDestroy());
+        ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0);
         options.setSplashScreenStyle(splashScreenStyle);
         options.setPendingIntentBackgroundActivityStartMode(
                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+        IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+        options.setOnAnimationAbortListener(endCallback);
+        options.setOnAnimationFinishedListener(endCallback);
+
         return new ActivityOptionsWrapper(options, callbacks);
     }
 
@@ -1179,25 +1175,15 @@
                     @Nullable Task foundTask = foundTasks[0];
                     if (foundTask != null) {
                         TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
-                        if (foundTaskView != null) {
-                            // The foundTaskView contains the 1-2 taskIds we are looking for.
-                            // If we are already in-app and running the correct tasks, no need
-                            // to do anything.
-                            if (FeatureFlags.enableAppPairs()
-                                    && isAlreadyInApp(foundTaskView.getTaskIds())) {
-                                return;
-                            }
-                            // If we are in Overview and the TaskView tile is visible, expand that
-                            // tile.
-                            if (foundTaskView.isVisibleToUser()) {
-                                TestLogging.recordEvent(
-                                        TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
-                                foundTaskView.launchTasks();
-                                return;
-                            }
+                        if (foundTaskView != null
+                                && foundTaskView.isVisibleToUser()) {
+                            TestLogging.recordEvent(
+                                    TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
+                            foundTaskView.launchTasks();
+                            return;
                         }
                     }
-                    // If none of the above cases apply, launch a new app or app pair.
+
                     if (findExactPairMatch) {
                         // We did not find the app pair we were looking for, so launch one.
                         recents.getSplitSelectController().getAppPairsController().launchAppPair(
@@ -1209,27 +1195,6 @@
         );
     }
 
-    /**
-     * Checks if a given list of taskIds are all already running in-app.
-     */
-    private boolean isAlreadyInApp(int[] ids) {
-        if (mControllers.uiController.isInOverview()) {
-            return false;
-        }
-
-        RunningTaskInfo[] currentlyRunningTasks = ActivityManagerWrapper.getInstance()
-                .getRunningTasks(false /* filterOnlyVisibleRecents */);
-        Set<Integer> currentlyRunningIds = Arrays.stream(currentlyRunningTasks)
-                .map(task -> task.taskId).collect(Collectors.toSet());
-
-        for (int id : ids) {
-            if (id != ActivityTaskManager.INVALID_TASK_ID && !currentlyRunningIds.contains(id)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private void startItemInfoActivity(ItemInfo info) {
         Intent intent = new Intent(info.getIntent())
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 9b73bb1..0991fce 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -54,6 +54,7 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
 import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
 import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -67,7 +68,6 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.res.Configuration;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.display.DisplayManager;
@@ -75,6 +75,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.AttributeSet;
@@ -145,7 +146,6 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.ObjectWrapper;
@@ -729,6 +729,10 @@
         return mSplitSelectStateController.isSplitSelectActive();
     }
 
+    public boolean areBothSplitAppsConfirmed() {
+        return mSplitSelectStateController.isBothSplitAppsConfirmed();
+    }
+
     @Override
     public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) {
         if (mTaskbarUIController != null) {
@@ -1136,13 +1140,14 @@
     @Override
     public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
         RunnableList callbacks = new RunnableList();
-        ActivityOptions options = ActivityOptions.makeCustomAnimation(
-                this, 0, 0, Color.TRANSPARENT,
-                Executors.MAIN_EXECUTOR.getHandler(), null,
-                elapsedRealTime -> callbacks.executeAllAndDestroy());
+        ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0);
         options.setSplashScreenStyle(splashScreenStyle);
         options.setPendingIntentBackgroundActivityStartMode(
                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+
+        IRemoteCallback endCallback = completeRunnableListCallback(callbacks);
+        options.setOnAnimationAbortListener(endCallback);
+        options.setOnAnimationFinishedListener(endCallback);
         return new ActivityOptionsWrapper(options, callbacks);
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 23e922c..27224f2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -17,6 +17,7 @@
 
 import static com.android.app.animation.Interpolators.LINEAR;
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
@@ -27,6 +28,7 @@
 import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
+import static com.android.wm.shell.Flags.enableSplitContextual;
 
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
@@ -120,7 +122,9 @@
      */
     private void handleSplitSelectionState(@NonNull LauncherState toState,
             @NonNull PendingAnimation builder, boolean animate) {
-        if (toState != OVERVIEW_SPLIT_SELECT) {
+        boolean goingToOverviewFromWorkspaceContextual = enableSplitContextual() &&
+                toState == OVERVIEW && mLauncher.isSplitSelectionActive();
+        if (toState != OVERVIEW_SPLIT_SELECT && !goingToOverviewFromWorkspaceContextual) {
             // Not going to split
             return;
         }
@@ -135,9 +139,11 @@
 
         SplitAnimationTimings timings =
                 AnimUtils.getDeviceOverviewToSplitTimings(mLauncher.getDeviceProfile().isTablet);
-
-        mRecentsView.createSplitSelectInitAnimation(builder,
-                toState.getTransitionDuration(mLauncher, true /* isToState */));
+        if (!goingToOverviewFromWorkspaceContextual) {
+            // This animation is already done for the contextual case, don't redo it
+            mRecentsView.createSplitSelectInitAnimation(builder,
+                    toState.getTransitionDuration(mLauncher, true /* isToState */));
+        }
         // Shift tasks vertically downward to get out of placeholder view
         builder.setFloat(mRecentsView, taskViewsFloat.first,
                 toState.getSplitSelectTranslation(mLauncher),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 396d0ab..7650235 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,6 +17,7 @@
 
 import static com.android.app.animation.Interpolators.DECELERATE_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
+import static com.android.wm.shell.Flags.enableSplitContextual;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -120,10 +121,22 @@
         if (showFloatingSearch) {
             elements |= FLOATING_SEARCH_BAR;
         }
+        if (enableSplitContextual() && launcher.isSplitSelectionActive()) {
+            elements &= ~CLEAR_ALL_BUTTON;
+        }
         return elements;
     }
 
     @Override
+    public float getSplitSelectTranslation(Launcher launcher) {
+        if (!enableSplitContextual() || !launcher.isSplitSelectionActive()) {
+            return 0f;
+        }
+        RecentsView recentsView = launcher.getOverviewPanel();
+        return recentsView.getSplitSelectTranslation();
+    }
+
+    @Override
     public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
         return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? 0
                 : super.getFloatingSearchBarRestingMarginBottom(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 19bfe06..ec84550 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -59,7 +59,7 @@
     private static final long MAX_TASK_DISMISS_ANIMATION_DURATION = 600;
 
     public static final int TASK_DISMISS_VIBRATION_PRIMITIVE =
-            Utilities.ATLEAST_R ? VibrationEffect.Composition.PRIMITIVE_TICK : -1;
+            VibrationEffect.Composition.PRIMITIVE_TICK;
     public static final float TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE = 1f;
     public static final VibrationEffect TASK_DISMISS_VIBRATION_FALLBACK =
             VibrationConstants.EFFECT_TEXTURE_TICK;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index cbf6ad6..60d0e2b 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -64,7 +64,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
@@ -75,7 +74,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.util.Log;
@@ -161,7 +159,6 @@
 /**
  * Handles the navigation gestures when Launcher is the default home activity.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
         Q extends RecentsView, S extends BaseState<S>>
         extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 2dd6a29..377b866 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -35,13 +35,11 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.os.Build;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
@@ -77,7 +75,6 @@
 /**
  * Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
  */
-@TargetApi(Build.VERSION_CODES.P)
 public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
         ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>> {
 
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 57b9a39..e77a450 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -29,7 +29,6 @@
 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 
 import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -37,7 +36,6 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -66,7 +64,6 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
-import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TransformParams;
@@ -82,7 +79,6 @@
 /**
  * Handles the navigation gestures when a 3rd party launcher is the default home activity.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class FallbackSwipeHandler extends
         AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> {
 
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 575be5f..d02909c 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -29,9 +29,7 @@
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
 
-import android.annotation.TargetApi;
 import android.content.Intent;
-import android.os.Build;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
 
@@ -58,7 +56,6 @@
  * Manages the state for an active system gesture, listens for events from the system and Launcher,
  * and fires events when the states change.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
 
     final Predicate<RemoteAnimationTarget> mLastStartedTaskIdPredicate = new Predicate<>() {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index b2429ad..315316d 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -18,10 +18,8 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
-import android.annotation.TargetApi;
 import android.content.Intent;
 import android.graphics.PointF;
-import android.os.Build;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.view.View;
@@ -51,7 +49,6 @@
 /**
  * Helper class to handle various atomic commands for switching between Overview.
  */
-@TargetApi(Build.VERSION_CODES.P)
 public class OverviewCommandHelper {
 
     public static final int TYPE_SHOW = 1;
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index d1939ef..3c902e6 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,10 +15,8 @@
  */
 package com.android.quickstep;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Looper;
 import android.os.Trace;
 import android.os.UserManager;
@@ -30,7 +28,6 @@
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 @SuppressWarnings("unused")
-@TargetApi(Build.VERSION_CODES.R)
 public class QuickstepProcessInitializer extends MainProcessInitializer {
 
     private static final String TAG = "QuickstepProcessInitializer";
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 7c263b8..f3704e09 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -23,12 +23,10 @@
 import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
 import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
 
-import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.app.TaskInfo;
 import android.content.ComponentName;
-import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.SparseBooleanArray;
@@ -53,7 +51,6 @@
 /**
  * Manages the recent task list from the system, caching it as necessary.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class RecentTasksList {
 
     private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 98d0ece..2e70703 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -20,11 +20,13 @@
 import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
 import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
 
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.Log;
 import android.view.RemoteAnimationTarget;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -35,6 +37,8 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Glues together the necessary components to animate a remote target using a
@@ -43,7 +47,9 @@
 public class RemoteTargetGluer {
     private static final String TAG = "RemoteTargetGluer";
 
-    private static final int DEFAULT_NUM_HANDLES = 2;
+    // This is the default number of handles to create when we don't know how many tasks are running
+    // (e.g. if we're in split screen). Allocate extra for potential tasks overlaid, like volume.
+    private static final int DEFAULT_NUM_HANDLES = 4;
 
     private RemoteTargetHandle[] mRemoteTargetHandles;
     private SplitConfigurationOptions.SplitBounds mSplitBounds;
@@ -107,7 +113,7 @@
         for (int i = 0; i < mRemoteTargetHandles.length; i++) {
             RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
             mRemoteTargetHandles[i].mTransformParams.setTargetSet(
-                    createRemoteAnimationTargetsForTarget(targets, null));
+                    createRemoteAnimationTargetsForTarget(targets, Collections.emptyList()));
             mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
         }
         return mRemoteTargetHandles;
@@ -177,18 +183,42 @@
             RemoteAnimationTarget topLeftTarget = targets.findTask(mSplitBounds.leftTopTaskId);
             RemoteAnimationTarget bottomRightTarget = targets.findTask(
                     mSplitBounds.rightBottomTaskId);
+            List<RemoteAnimationTarget> overlayTargets = Arrays.stream(targets.apps).filter(
+                    target -> target.windowConfiguration.getWindowingMode()
+                            != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW).toList();
 
             // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
             // vice versa
             mRemoteTargetHandles[0].mTransformParams.setTargetSet(
-                    createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
-            mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
-                    mSplitBounds);
+                    createRemoteAnimationTargetsForTarget(targets,
+                            Collections.singletonList(bottomRightTarget)));
+            mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, mSplitBounds);
 
             mRemoteTargetHandles[1].mTransformParams.setTargetSet(
-                    createRemoteAnimationTargetsForTarget(targets, topLeftTarget));
-            mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget,
-                    mSplitBounds);
+                    createRemoteAnimationTargetsForTarget(targets,
+                            Collections.singletonList(topLeftTarget)));
+            mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget, mSplitBounds);
+
+            // Set the remaining overlay tasks to be their own TaskViewSimulator as fullscreen tasks
+            if (!overlayTargets.isEmpty()) {
+                ArrayList<RemoteAnimationTarget> targetsToExclude = new ArrayList<>();
+                targetsToExclude.add(topLeftTarget);
+                targetsToExclude.add(bottomRightTarget);
+                // Start i at 2 to account for top/left and bottom/right split handles already made
+                for (int i = 2; i < targets.apps.length; i++) {
+                    if (i >= mRemoteTargetHandles.length) {
+                        Log.e(TAG, String.format("Attempting to animate an untracked target"
+                                + " (%d handles allocated, but %d want to animate)",
+                                mRemoteTargetHandles.length, targets.apps.length));
+                        break;
+                    }
+                    mRemoteTargetHandles[i].mTransformParams.setTargetSet(
+                            createRemoteAnimationTargetsForTarget(targets, targetsToExclude));
+                    mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(
+                            overlayTargets.get(i - 2));
+                }
+
+            }
         }
         return mRemoteTargetHandles;
     }
@@ -214,32 +244,38 @@
     /**
      * Ensures that we aren't excluding ancillary targets such as home/recents
      *
-     * @param targetToExclude Will be excluded from the resulting return value.
-     *                        Pass in {@code null} to not exclude anything
+     * @param targetsToExclude Will be excluded from the resulting return value.
+     *                        Pass in an empty list to not exclude anything
      * @return RemoteAnimationTargets where all the app targets from the passed in
-     *         {@param targets} are included except {@param targetToExclude}
+     *         {@code targets} are included except {@code targetsToExclude}
      */
     private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
-            RemoteAnimationTargets targets,
-            RemoteAnimationTarget targetToExclude) {
-        ArrayList<RemoteAnimationTarget> targetsWithoutExcluded = new ArrayList<>();
+            @NonNull RemoteAnimationTargets targets,
+            @NonNull List<RemoteAnimationTarget> targetsToExclude) {
+        ArrayList<RemoteAnimationTarget> targetsToInclude = new ArrayList<>();
 
         for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
-            if (targetCompat == targetToExclude) {
+            boolean skipTarget = false;
+            for (RemoteAnimationTarget excludingTarget : targetsToExclude) {
+                if (targetCompat == excludingTarget) {
+                    skipTarget = true;
+                    break;
+                }
+                if (excludingTarget != null
+                        && excludingTarget.taskInfo != null
+                        && targetCompat.taskInfo != null
+                        && excludingTarget.taskInfo.parentTaskId == targetCompat.taskInfo.taskId) {
+                    // Also exclude corresponding parent task
+                    skipTarget = true;
+                }
+            }
+            if (skipTarget) {
                 continue;
             }
-            if (targetToExclude != null
-                    && targetToExclude.taskInfo != null
-                    && targetCompat.taskInfo != null
-                    && targetToExclude.taskInfo.parentTaskId == targetCompat.taskInfo.taskId) {
-                // Also exclude corresponding parent task
-                continue;
-            }
-
-            targetsWithoutExcluded.add(targetCompat);
+            targetsToInclude.add(targetCompat);
         }
-        final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray(
-                new RemoteAnimationTarget[targetsWithoutExcluded.size()]);
+        final RemoteAnimationTarget[] filteredApps = targetsToInclude.toArray(
+                new RemoteAnimationTarget[0]);
         return new RemoteAnimationTargets(
                 filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode);
     }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 4e84f4a..8ff43f0 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -43,14 +43,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Matrix.ScaleToFit;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -94,7 +92,6 @@
 /**
  * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public final class TaskViewUtils {
 
     private TaskViewUtils() {}
@@ -196,12 +193,13 @@
                 remoteTargetHandles = gluer.assignTargets(targets);
             }
         }
+
         final int recentsActivityRotation =
                 recentsView.getPagedViewOrientedState().getRecentsActivityRotation();
-        for (RemoteTargetHandle remoteTargetGluer : remoteTargetHandles) {
-            remoteTargetGluer.getTaskViewSimulator().getOrientationState().setRecentsRotation(
-                    recentsActivityRotation);
-            remoteTargetGluer.getTransformParams().setSyncTransactionApplier(applier);
+        for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) {
+            remoteTargetHandle.getTaskViewSimulator().getOrientationState()
+                    .setRecentsRotation(recentsActivityRotation);
+            remoteTargetHandle.getTransformParams().setSyncTransactionApplier(applier);
         }
 
         int taskIndex = recentsView.indexOfChild(v);
@@ -394,6 +392,13 @@
 
         out.addListener(new AnimationSuccessListener() {
             @Override
+            public void onAnimationStart(Animator animation) {
+                for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) {
+                    remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
+                }
+            }
+
+            @Override
             public void onAnimationSuccess(Animator animator) {
                 if (isQuickSwitch) {
                     InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ce6ddd8..55a7985 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -56,7 +56,6 @@
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
 
-import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.Service;
@@ -66,7 +65,6 @@
 import android.content.res.Configuration;
 import android.graphics.Region;
 import android.graphics.drawable.Icon;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
@@ -152,7 +150,6 @@
 /**
  * Service connected by system-UI for handling touch interaction.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class TouchInteractionService extends Service {
 
     private static final String SUBSTRING_PREFIX = "; ";
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 1008da3..0ee50a4 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -24,9 +24,7 @@
 import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
 
 import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
 import android.content.Context;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 
@@ -55,7 +53,6 @@
 
 import java.util.ArrayList;
 
-@TargetApi(Build.VERSION_CODES.R)
 public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
         implements StateListener<RecentsState> {
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index eedd204..c8c7dc2 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -31,12 +31,10 @@
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.graphics.PointF;
-import android.os.Build;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -71,7 +69,6 @@
 /**
  * Input consumer for handling events originating from an activity other than Launcher
  */
-@TargetApi(Build.VERSION_CODES.P)
 public class OtherActivityInputConsumer extends ContextWrapper implements InputConsumer {
 
     public static final String DOWN_EVT = "OtherActivityInputConsumer.DOWN";
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index a36f501..4198e2d 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -54,6 +54,8 @@
     static final String KEY_TUTORIAL_TYPE = "tutorial_type";
     static final String KEY_GESTURE_COMPLETE = "gesture_complete";
     static final String KEY_USE_TUTORIAL_MENU = "use_tutorial_menu";
+    public static final double SQUARE_ASPECT_RATIO_BOTTOM_BOUND = 0.95;
+    public static final double SQUARE_ASPECT_RATIO_UPPER_BOUND = 1.05;
 
     @Nullable private TutorialType[] mTutorialSteps;
     private GestureSandboxFragment mCurrentFragment;
@@ -159,14 +161,20 @@
      * Gesture animations are only in landscape for large screens and portrait for mobile. This
      * method enforces the following flows:
      *     1) phone / two-panel closed -> lock to portrait
-     *     2) two-panel open / tablet + portrait -> prompt the user to rotate the screen
-     *     3) two-panel open / tablet + landscape -> hide potential rotating prompt
+     *     2) Large screen + portrait -> prompt the user to rotate the screen
+     *     3) Large screen + landscape -> hide potential rotating prompt
+     *     4) Square aspect ratio -> no action taken as the animations will fit both orientations
      */
     private void correctUserOrientation() {
         DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
                 getApplicationContext()).getDeviceProfile(this);
         if (deviceProfile.isTablet) {
-            boolean showRotationPrompt = getResources().getConfiguration().orientation
+            // The tutorial will work in either orientation if the height and width are similar
+            boolean isAspectRatioSquare =
+                    deviceProfile.aspectRatio > SQUARE_ASPECT_RATIO_BOTTOM_BOUND
+                            && deviceProfile.aspectRatio < SQUARE_ASPECT_RATIO_UPPER_BOUND;
+            boolean showRotationPrompt = !isAspectRatioSquare
+                    && getResources().getConfiguration().orientation
                     == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 
             GestureSandboxFragment recreatedFragment =
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index df552cf..1129e02 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -17,9 +17,7 @@
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
 
-import android.annotation.TargetApi;
 import android.graphics.PointF;
-import android.os.Build;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -30,7 +28,6 @@
 import java.util.Map;
 
 /** A {@link TutorialController} for the Home tutorial. */
-@TargetApi(Build.VERSION_CODES.R)
 final class HomeGestureTutorialController extends SwipeUpGestureTutorialController {
 
     HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) {
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 65f3a01..a04dd44 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -21,9 +21,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
 import android.graphics.PointF;
-import android.os.Build;
 import android.os.Handler;
 
 import androidx.annotation.ColorInt;
@@ -42,7 +40,6 @@
 import java.util.Map;
 
 /** A {@link TutorialController} for the Overview tutorial. */
-@TargetApi(Build.VERSION_CODES.R)
 final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController {
 
     private static final float LAUNCHER_COLOR_BLENDING_RATIO = 0.4f;
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 87defc5..d5cc447 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -29,13 +29,11 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Outline;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
@@ -63,7 +61,6 @@
 import com.android.quickstep.util.SurfaceTransaction.MockProperties;
 import com.android.quickstep.util.TransformParams;
 
-@TargetApi(Build.VERSION_CODES.R)
 abstract class SwipeUpGestureTutorialController extends TutorialController {
 
     private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(24);
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index cf9fc74..d265918 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.isRunningInTestHarness()) {
+        if (Utilities.isRunningInTestHarness()) {
             return;
         }
         SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
@@ -342,9 +342,6 @@
 
         @Override
         public void log(EventEnum event) {
-            if (!Utilities.ATLEAST_R) {
-                return;
-            }
             if (DEBUG) {
                 String name = (event instanceof Enum) ? ((Enum) event).name() :
                         event.getId() + "";
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index 7fbbb6e..1f2a02c 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -16,6 +16,13 @@
 
 package com.android.quickstep.util;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+
+import com.android.launcher3.util.RunnableList;
+
 /**
  * Utility class containing methods to help manage animations, interpolators, and timings.
  */
@@ -48,4 +55,16 @@
                 ? SplitAnimationTimings.TABLET_APP_PAIR_LAUNCH
                 : SplitAnimationTimings.PHONE_APP_PAIR_LAUNCH;
     }
+
+    /**
+     * Returns a IRemoteCallback which completes the provided list as a result
+     */
+    public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
+        return new IRemoteCallback.Stub() {
+            @Override
+            public void sendResult(Bundle bundle) {
+                MAIN_EXECUTOR.execute(list::executeAllAndDestroy);
+            }
+        };
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index df5765c..a26d056 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep.util;
 
-import android.annotation.TargetApi;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.view.SurfaceControl;
@@ -34,7 +32,6 @@
  *   android.view.SyncRtSurfaceTransactionApplier
  * with some Launcher specific utility methods
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class SurfaceTransactionApplier extends ReleaseCheck {
 
     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 57b265b..937a728 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.Utilities.prefixTextWithIcon;
 import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
 
-import android.annotation.TargetApi;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
@@ -33,7 +32,6 @@
 import android.icu.text.MeasureFormat.FormatWidth;
 import android.icu.util.Measure;
 import android.icu.util.MeasureUnit;
-import android.os.Build;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Pair;
@@ -61,7 +59,6 @@
 import java.time.Duration;
 import java.util.Locale;
 
-@TargetApi(Build.VERSION_CODES.Q)
 public final class DigitalWellBeingToast {
 
     private static final float THRESHOLD_LEFT_ICON_ONLY = 0.4f;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 997624f..6b3484d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -81,7 +81,6 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.Intent;
@@ -98,7 +97,6 @@
 import android.graphics.RectF;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -231,7 +229,6 @@
 /**
  * A list of recent tasks.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
         STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index f39a901..c4b93b7 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -120,10 +120,12 @@
 
     private void init() {
         TextView cancelTextView = findViewById(R.id.split_instructions_text_cancel);
+        TextView instructionTextView = findViewById(R.id.split_instructions_text);
 
         if (FeatureFlags.enableSplitContextually()) {
             cancelTextView.setVisibility(VISIBLE);
             cancelTextView.setOnClickListener((v) -> exitSplitSelection());
+            instructionTextView.setText(R.string.toast_contextual_split_select_app);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 66a880b..68710a7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -57,7 +57,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -119,8 +118,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 
-import kotlin.Unit;
-
 import java.lang.annotation.Retention;
 import java.util.Arrays;
 import java.util.Collections;
@@ -129,6 +126,8 @@
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
+import kotlin.Unit;
+
 /**
  * A task in the Recents view.
  */
@@ -970,20 +969,6 @@
     }
 
     /**
-     * Returns ActivityOptions for overriding task transition animation.
-     */
-    private ActivityOptions makeCustomAnimation(Context context, int enterResId,
-            int exitResId, final Runnable callback, final Handler callbackHandler) {
-        return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
-                callbackHandler,
-                elapsedRealTime -> {
-                    if (callback != null) {
-                        callbackHandler.post(callback);
-                    }
-                }, null /* finishedListener */);
-    }
-
-    /**
      * Launch of the current task (both live and inactive tasks) with an animation.
      */
     @Nullable
@@ -1034,15 +1019,6 @@
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationStart(Animator animation) {
-                    recentsView.runActionOnRemoteHandles(
-                            (Consumer<RemoteTargetHandle>) remoteTargetHandle ->
-                                    remoteTargetHandle
-                                            .getTaskViewSimulator()
-                                            .setDrawsBelowRecents(false));
-                }
-
-                @Override
                 public void onAnimationEnd(Animator animator) {
                     if (mTask != null && mTask.key.displayId != getRootViewDisplayId()) {
                         launchTaskAnimated();
@@ -1348,10 +1324,8 @@
             setPivotX((right - left) * 0.5f);
             setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
         }
-        if (Utilities.ATLEAST_Q) {
-            SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
-            setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
-        }
+        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
+        setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index a71d74a..df73e09 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -15,18 +15,14 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
 
 import android.graphics.Rect;
 
 import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
@@ -34,9 +30,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.Closeable;
-import java.io.IOException;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TaplTestsPersistentTaskbar extends AbstractTaplTestsTaskbar {
@@ -51,27 +44,20 @@
     }
 
     @Test
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/320490387
     @NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
     public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
-        // Start off in light mode.
-        try (Closeable c = InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .executeShellCommand("cmd uimode night no")) {
-            Rect taskbarBoundsBefore = getTaskbar().getVisibleBounds();
-            startImeTestActivity();
-            // IME should stash the taskbar, which hides icons even in 3 button mode.
-            mLauncher.getLaunchedAppState().assertTaskbarHidden();
-            // Switch to dark mode (any configuration change here would do).
-            InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
-                    "cmd uimode night yes").close();
-            // Close IME to check new taskbar bounds.
-            mLauncher.pressBack();
-            Rect taskbarBoundsAfter = getTaskbar().getVisibleBounds();
-            Assert.assertEquals(
-                    "Taskbar bounds are not the same after a configuration change while stashed.",
-                    taskbarBoundsBefore, taskbarBoundsAfter);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        Rect taskbarBoundsBefore = getTaskbar().getVisibleBounds();
+        // Go home and to an IME activity (any configuration change would do, as long as it
+        // triggers taskbar insets or height change while taskbar is stashed).
+        mLauncher.goHome();
+        startImeTestActivity();
+        // IME should stash the taskbar, which hides icons even in 3 button mode.
+        mLauncher.getLaunchedAppState().assertTaskbarHidden();
+        // Close IME to check new taskbar bounds.
+        startTestActivity(2);
+        Rect taskbarBoundsAfter = getTaskbar().getVisibleBounds();
+        Assert.assertEquals(
+                "Taskbar bounds are not the same after a configuration change while stashed.",
+                taskbarBoundsBefore, taskbarBoundsAfter);
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 1b8866a..fcb8320 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -55,7 +55,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -334,9 +333,6 @@
 
     @Test
     @TaskbarModeSwitch
-    @ScreenRecord // b/314873201
-    // Staging; will be promoted to presubmit if stable
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
     public void testQuickSwitchToPreviousAppForTablet() throws Exception {
         assumeTrue(mLauncher.isTablet());
         startTestActivity(2);
diff --git a/res/drawable/private_space_app_divider.xml b/res/drawable/private_space_app_divider.xml
new file mode 100644
index 0000000..7d069ef
--- /dev/null
+++ b/res/drawable/private_space_app_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/materialColorOutlineVariant"/>
+    <size android:height="1dp" />
+</shape>
\ No newline at end of file
diff --git a/res/layout/private_space_divider.xml b/res/layout/private_space_divider.xml
new file mode 100644
index 0000000..fff8629
--- /dev/null
+++ b/res/layout/private_space_divider.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 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.
+  -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/private_space_divider"
+    android:importantForAccessibility="no"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="@dimen/ps_app_divider_padding"
+    android:paddingRight="@dimen/ps_app_divider_padding"
+    android:src="@drawable/private_space_app_divider"
+    android:scaleType="fitXY"
+    android:focusable="false" />
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 603e697..3016559 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -487,4 +487,5 @@
     <dimen name="ps_button_height">36dp</dimen>
     <dimen name="ps_button_width">36dp</dimen>
     <dimen name="ps_lock_button_width">89dp</dimen>
+    <dimen name="ps_app_divider_padding">16dp</dimen>
 </resources>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 79b831e..4a277f0 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -191,14 +191,12 @@
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
-        if (Utilities.ATLEAST_Q) {
-            for (int i = 0; i < HANDLE_COUNT; i++) {
-                View dragHandle = mDragHandles[i];
-                mSystemGestureExclusionRects.get(i).set(dragHandle.getLeft(), dragHandle.getTop(),
-                        dragHandle.getRight(), dragHandle.getBottom());
-            }
-            setSystemGestureExclusionRects(mSystemGestureExclusionRects);
+        for (int i = 0; i < HANDLE_COUNT; i++) {
+            View dragHandle = mDragHandles[i];
+            mSystemGestureExclusionRects.get(i).set(dragHandle.getLeft(), dragHandle.getTop(),
+                    dragHandle.getRight(), dragHandle.getBottom());
         }
+        setSystemGestureExclusionRects(mSystemGestureExclusionRects);
     }
 
     public static void showForWidget(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index b6f6615..1c2ed43 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -20,11 +20,8 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.ActionMode;
-import android.view.Display;
 import android.view.View;
 
 import androidx.annotation.MainThread;
@@ -165,15 +162,7 @@
     protected abstract void reapplyUi();
 
     protected WindowBounds getMultiWindowDisplaySize() {
-        if (Utilities.ATLEAST_R) {
-            return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
-        }
-        // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
-        // the app window size
-        Display display = getWindowManager().getDefaultDisplay();
-        Point mwSize = new Point();
-        display.getSize(mwSize);
-        return new WindowBounds(new Rect(0, 0, mwSize.x, mwSize.y), new Rect());
+        return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
     }
 
     @Override
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 6d2fbb4..1b4edf8 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -16,18 +16,13 @@
 
 package com.android.launcher3;
 
-import static android.animation.ValueAnimator.areAnimatorsEnabled;
-
-import static com.android.app.animation.Interpolators.DECELERATE_1_5;
 import static com.android.launcher3.LauncherState.EDIT_MODE;
 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;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -70,6 +65,7 @@
 import com.android.launcher3.celllayout.ItemConfiguration;
 import com.android.launcher3.celllayout.ReorderAlgorithm;
 import com.android.launcher3.celllayout.ReorderParameters;
+import com.android.launcher3.celllayout.ReorderPreviewAnimation;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.PreviewBackground;
@@ -188,7 +184,7 @@
 
     @ContainerType private final int mContainerType;
 
-    private final float mChildScale = 1f;
+    public static final float DEFAULT_SCALE = 1f;
 
     public static final int MODE_SHOW_REORDER_HINT = 0;
     public static final int MODE_DRAG_OVER = 1;
@@ -198,8 +194,8 @@
     private static final boolean DESTRUCTIVE_REORDER = false;
     private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
 
-    private static final float REORDER_PREVIEW_MAGNITUDE = 0.12f;
-    private static final int REORDER_ANIMATION_DURATION = 150;
+    public static final float REORDER_PREVIEW_MAGNITUDE = 0.12f;
+    public static final int REORDER_ANIMATION_DURATION = 150;
     @Thunk final float mReorderPreviewAnimationMagnitude;
 
     private final ArrayList<View> mIntersectingViews = new ArrayList<>();
@@ -761,8 +757,8 @@
             bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
         }
 
-        child.setScaleX(mChildScale);
-        child.setScaleY(mChildScale);
+        child.setScaleX(DEFAULT_SCALE);
+        child.setScaleY(DEFAULT_SCALE);
 
         // Generate an id for each view, this assumes we have at most 256x256 cells
         // per workspace screen
@@ -1438,147 +1434,14 @@
 
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
             if (c != null && !skip && (child instanceof Reorderable)) {
-                ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child,
-                        mode, lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY);
+                ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child, mode,
+                        lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY,
+                        mReorderPreviewAnimationMagnitude, this, mShakeAnimators);
                 rha.animate();
             }
         }
     }
 
-    // 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<T extends View & Reorderable> implements
-            ValueAnimator.AnimatorUpdateListener {
-        final T child;
-        float finalDeltaX;
-        float finalDeltaY;
-        float initDeltaX;
-        float initDeltaY;
-        final float finalScale;
-        float initScale;
-        private static final int PREVIEW_DURATION = 300;
-        private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT;
-
-        private static final float CHILD_DIVIDEND = 4.0f;
-
-        public static final int MODE_HINT = 0;
-        public static final int MODE_PREVIEW = 1;
-
-        ValueAnimator mAnimator;
-
-        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];
-            final int y0 = mTmpPoint[1];
-            regionToCenterPoint(cellX1, cellY1, spanX, spanY, mTmpPoint);
-            final int x1 = mTmpPoint[0];
-            final int y1 = mTmpPoint[1];
-            final int dX = x1 - x0;
-            final int dY = y1 - y0;
-
-            this.child = (T) childView;
-            finalDeltaX = 0;
-            finalDeltaY = 0;
-
-            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.getWidth()) * initScale;
-
-            mAnimator = ObjectAnimator.ofFloat(0, 1);
-            mAnimator.addUpdateListener(this);
-
-            // Animations are disabled in power save mode, causing the repeated animation to jump
-            // spastically between beginning and end states. Since this looks bad, we don't repeat
-            // the animation in power save mode.
-            if (areAnimatorsEnabled() && mode == MODE_PREVIEW) {
-                mAnimator.setRepeatCount(ValueAnimator.INFINITE);
-                mAnimator.setRepeatMode(ValueAnimator.REVERSE);
-            }
-
-            mAnimator.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION);
-            mAnimator.setStartDelay((int) (Math.random() * 60));
-
-            int dir = mode == MODE_HINT ? -1 : 1;
-            if (dX == dY && dX == 0) {
-            } else {
-                if (dY == 0) {
-                    finalDeltaX = -dir * Math.signum(dX) * mReorderPreviewAnimationMagnitude;
-                } else if (dX == 0) {
-                    finalDeltaY = -dir * Math.signum(dY) * mReorderPreviewAnimationMagnitude;
-                } else {
-                    double angle = Math.atan( (float) (dY) / dX);
-                    finalDeltaX = (int) (-dir * Math.signum(dX)
-                            * Math.abs(Math.cos(angle) * mReorderPreviewAnimationMagnitude));
-                    finalDeltaY = (int) (-dir * Math.signum(dY)
-                            * Math.abs(Math.sin(angle) * mReorderPreviewAnimationMagnitude));
-                }
-            }
-        }
-
-        void setInitialAnimationValuesToBaseline() {
-            initScale = mChildScale;
-            initDeltaX = 0;
-            initDeltaY = 0;
-        }
-
-        void animate() {
-            boolean noMovement = (finalDeltaX == 0) && (finalDeltaY == 0);
-
-            if (mShakeAnimators.containsKey(child)) {
-                ReorderPreviewAnimation oldAnimation = mShakeAnimators.get(child);
-                mShakeAnimators.remove(child);
-
-                if (noMovement) {
-                    // A previous animation for this item exists, and no new animation will exist.
-                    // Finish the old animation smoothly.
-                    oldAnimation.finishAnimation();
-                    return;
-                } else {
-                    // A previous animation for this item exists, and a new one will exist. Stop
-                    // the old animation in its tracks, and proceed with the new one.
-                    oldAnimation.cancel();
-                }
-            }
-            if (noMovement) {
-                return;
-            }
-
-            mShakeAnimators.put(child, this);
-            mAnimator.start();
-        }
-
-        @Override
-        public void onAnimationUpdate(ValueAnimator updatedAnimation) {
-            float progress = (float) updatedAnimation.getAnimatedValue();
-            child.getTranslateDelegate().setTranslation(
-                    INDEX_REORDER_BOUNCE_OFFSET,
-                    /* dx = */ progress * finalDeltaX + (1 - progress) * initDeltaX,
-                    /* dy = */ progress * finalDeltaY + (1 - progress) * initDeltaY
-            );
-            child.setReorderBounceScale(progress * finalScale + (1 - progress) * initScale);
-        }
-
-        private void cancel() {
-            mAnimator.cancel();
-        }
-
-        /**
-         * Smoothly returns the item to its baseline position / scale
-         */
-        @Thunk void finishAnimation() {
-            mAnimator.cancel();
-            setInitialAnimationValuesToBaseline();
-            mAnimator = ObjectAnimator.ofFloat((Float) mAnimator.getAnimatedValue(), 0);
-            mAnimator.addUpdateListener(this);
-            mAnimator.setInterpolator(DECELERATE_1_5);
-            mAnimator.setDuration(REORDER_ANIMATION_DURATION);
-            mAnimator.start();
-        }
-    }
-
     private void completeAndClearReorderPreviewAnimations() {
         for (ReorderPreviewAnimation a: mShakeAnimators.values()) {
             a.finishAnimation();
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index ec26f58..fe9348c 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.text.TextUtils;
@@ -93,7 +91,6 @@
      * @return true if the keyboard is shown correctly and focus is given to this view.
      */
     public boolean showKeyboard() {
-        onKeyboardShown();
         return requestFocus() && showSoftInputInternal();
     }
 
@@ -120,11 +117,6 @@
         }
     }
 
-    protected void onKeyboardShown() {
-        ActivityContext.lookupContext(getContext()).getStatsLogManager()
-                .keyboardStateManager().setKeyboardState(SHOW);
-    }
-
     private boolean showSoftInputInternal() {
         boolean result = false;
         InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index a13dcc1..7ec0a89 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -197,11 +197,11 @@
     /**
      * Scrolls this recycler view to the bottom with easing and duration.
      */
-    public void scrollToBottomWithMotion() {
+    public void scrollToBottomWithMotion(int duration) {
         if (mScrollbar != null) {
             mScrollbar.reattachThumbToScroll();
         }
         // Emphasized interpolators with 500ms duration
-        smoothScrollBy(0, getAvailableScrollHeight(), Interpolators.EMPHASIZED, 500);
+        smoothScrollBy(0, getAvailableScrollHeight(), Interpolators.EMPHASIZED, duration);
     }
 }
diff --git a/src/com/android/launcher3/GestureNavContract.java b/src/com/android/launcher3/GestureNavContract.java
index c782dca..9ef6edc 100644
--- a/src/com/android/launcher3/GestureNavContract.java
+++ b/src/com/android/launcher3/GestureNavContract.java
@@ -20,11 +20,9 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
 
-import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.RectF;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -69,7 +67,6 @@
     /**
      * Sends the position information to the receiver
      */
-    @TargetApi(Build.VERSION_CODES.R)
     public void sendEndPosition(RectF position, ActivityContext context,
             @Nullable SurfaceControl surfaceControl) {
         Bundle result = new Bundle();
@@ -95,9 +92,6 @@
      * Clears and returns the GestureNavContract if it was present in the intent.
      */
     public static GestureNavContract fromIntent(Intent intent) {
-        if (!Utilities.ATLEAST_R) {
-            return null;
-        }
         Bundle extras = intent.getBundleExtra(EXTRA_GESTURE_CONTRACT);
         if (extras == null) {
             return null;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5b5d4cb..44a1bf0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -19,6 +19,7 @@
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
 import static com.android.app.animation.Interpolators.EMPHASIZED;
@@ -54,7 +55,6 @@
 import static com.android.launcher3.LauncherConstants.TraceEvents.ON_START_EVT;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.EDIT_MODE;
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
@@ -67,6 +67,8 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -139,6 +141,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
@@ -219,6 +223,7 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.ItemInflater;
 import com.android.launcher3.util.KeyboardShortcutsDelegate;
 import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.PackageUserKey;
@@ -246,8 +251,6 @@
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
-import com.android.launcher3.widget.WidgetInflater;
-import com.android.launcher3.widget.WidgetInflater.InflationResult;
 import com.android.launcher3.widget.WidgetManagerHelper;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -329,7 +332,7 @@
 
     private WidgetManagerHelper mAppWidgetManager;
     private LauncherWidgetHolder mAppWidgetHolder;
-    private WidgetInflater mWidgetInflater;
+    private ItemInflater<Launcher> mItemInflater;
 
     private final int[] mTmpAddItemCellCoordinates = new int[2];
 
@@ -515,10 +518,11 @@
         updateDisallowBack();
 
         mAppWidgetManager = new WidgetManagerHelper(this);
-        mWidgetInflater = new WidgetInflater(this);
         mAppWidgetHolder = createAppWidgetHolder();
         mAppWidgetHolder.startListening();
         mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null));
+        mItemInflater = new ItemInflater<>(this, mAppWidgetHolder, getItemOnClickListener(),
+                mFocusHandler, new CellLayout(mWorkspace.getContext()));
 
         mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 
@@ -576,9 +580,7 @@
         mRotationHelper.initialize();
         TraceHelper.INSTANCE.endSection();
 
-        if (Utilities.ATLEAST_R) {
-            getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
-        }
+        getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
         setTitle(R.string.home_screen);
         mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
 
@@ -1084,6 +1086,25 @@
 
         DiscoveryBounce.showForHomeIfNeeded(this);
         mAppWidgetHolder.setActivityResumed(true);
+
+        // Listen for IME changes to keep state up to date.
+        getRootView().setWindowInsetsAnimationCallback(
+                new WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
+                    @Override
+                    public WindowInsets onProgress(WindowInsets windowInsets,
+                            List<WindowInsetsAnimation> windowInsetsAnimations) {
+                        return windowInsets;
+                    }
+
+                    @Override
+                    public void onEnd(WindowInsetsAnimation animation) {
+                        WindowInsets insets = getRootView().getRootWindowInsets();
+                        boolean isImeVisible =
+                                insets != null && insets.isVisible(WindowInsets.Type.ime());
+                        getStatsLogManager().keyboardStateManager().setKeyboardState(
+                                isImeVisible ? SHOW : HIDE);
+                    }
+                });
     }
 
     private void logStopAndResume(boolean isResume) {
@@ -1354,35 +1375,6 @@
     }
 
     /**
-     * Creates a view representing a shortcut.
-     *
-     * @param info The data structure describing the shortcut.
-     */
-    View createShortcut(WorkspaceItemInfo info) {
-        // This can be called before PagedView#pageScrollsInitialized returns true, so use the
-        // first page, which we always assume to be present.
-        return createShortcut((ViewGroup) mWorkspace.getChildAt(0), info);
-    }
-
-    /**
-     * Creates a view representing a shortcut inflated from the specified resource.
-     *
-     * @param parent The group the shortcut belongs to. This is not necessarily the group where
-     *               the shortcut should be added.
-     * @param info   The data structure describing the shortcut.
-     * @return A View inflated from layoutResId.
-     */
-    public View createShortcut(@Nullable ViewGroup parent, WorkspaceItemInfo info) {
-        BubbleTextView favorite =
-                (BubbleTextView) LayoutInflater.from(parent != null ? parent.getContext() : this)
-                        .inflate(R.layout.app_icon, parent, false);
-        favorite.applyFromWorkspaceItem(info);
-        favorite.setOnClickListener(getItemOnClickListener());
-        favorite.setOnFocusChangeListener(mFocusHandler);
-        return favorite;
-    }
-
-    /**
      * Add a shortcut to the workspace or to a Folder.
      *
      * @param data The intent describing the shortcut.
@@ -1405,7 +1397,7 @@
 
         if (container < 0) {
             // Adding a shortcut to the Workspace.
-            final View view = createShortcut(info);
+            final View view = mItemInflater.inflateItem(info, getModelWriter());
             boolean foundCellSpan = false;
             // First we check if we already know the exact location where we want to add this item.
             if (cellX >= 0 && cellY >= 0) {
@@ -1491,7 +1483,7 @@
                 itemInfo.container, presenterPos.screenId, presenterPos.cellX, presenterPos.cellY);
 
         hostView.setVisibility(View.VISIBLE);
-        prepareAppWidget(hostView, launcherInfo);
+        mItemInflater.prepareAppWidget(hostView, launcherInfo);
         mWorkspace.addInScreen(hostView, launcherInfo);
         announceForAccessibility(R.string.item_added_to_workspace);
 
@@ -1516,12 +1508,6 @@
         }
     }
 
-    private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
-        hostView.setTag(item);
-        hostView.setFocusable(true);
-        hostView.setOnFocusChangeListener(mFocusHandler);
-    }
-
     private final ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
 
     private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
@@ -2157,63 +2143,19 @@
             final boolean focusFirstItemForAccessibility) {
         // Get the list of added items and intersect them with the set of items here
         final Collection<Animator> bounceAnims = new ArrayList<>();
-        boolean canAnimatePageChange = canAnimatePageChange();
         Workspace<?> workspace = mWorkspace;
         int newItemsScreenId = -1;
         int end = items.size();
         View newView = null;
         for (int i = 0; i < end; i++) {
             final ItemInfo item = items.get(i);
-            // Short circuit if we are loading dock items for a configuration which has no dock
-            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
-                    mHotseat == null) {
-                continue;
-            }
 
-            final View view;
-            switch (item.itemType) {
-                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
-                    WorkspaceItemInfo info = (WorkspaceItemInfo) item;
-                    view = createShortcut(info);
-                    break;
-                }
-                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
-                    view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
-                            (FolderInfo) item);
-                    break;
-                }
-                case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR: {
-                    view = AppPairIcon.inflateIcon(R.layout.app_pair_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
-                            (FolderInfo) item);
-                    break;
-                }
-                case ITEM_TYPE_APPWIDGET:
-                case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
-                    view = inflateAppWidget((LauncherAppWidgetInfo) item);
-                    if (view == null) {
-                        continue;
-                    }
-                    break;
-                }
-                default:
-                    throw new RuntimeException("Invalid Item Type");
-            }
-
-            /*
-             * Remove colliding items.
-             */
+            // Remove colliding items.
             CellPos presenterPos = getCellPosMapper().mapModelToPresenter(item);
             if (item.container == CONTAINER_DESKTOP) {
                 CellLayout cl = mWorkspace.getScreenWithId(presenterPos.screenId);
                 if (cl != null && cl.isOccupied(presenterPos.cellX, presenterPos.cellY)) {
-                    View v = cl.getChildAt(presenterPos.cellX, presenterPos.cellY);
-                    if (v == null) {
-                        Log.e(TAG, "bindItems failed when removing colliding item=" + item);
-                    }
-                    Object tag = v.getTag();
+                    Object tag = cl.getChildAt(presenterPos.cellX, presenterPos.cellY).getTag();
                     String desc = "Collision while binding workspace item: " + item
                             + ". Collides with " + tag;
                     if (FeatureFlags.IS_STUDIO_BUILD) {
@@ -2224,6 +2166,11 @@
                     }
                 }
             }
+
+            final View view = mItemInflater.inflateItem(item, getModelWriter());
+            if (view == null) {
+                continue;
+            }
             workspace.addInScreenFromBind(view, item);
             if (forceAnimateIcons) {
                 // Animate all the applications up now
@@ -2240,7 +2187,7 @@
         }
 
         View viewToFocus = newView;
-        // Animate to the correct pager
+        // Animate to the correct page
         if (forceAnimateIcons && newItemsScreenId > -1) {
             AnimatorSet anim = new AnimatorSet();
             anim.playTogether(bounceAnims);
@@ -2257,19 +2204,13 @@
             final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
             final Runnable startBounceAnimRunnable = anim::start;
 
-            if (canAnimatePageChange && newItemsScreenId != currentScreenId) {
+            if (canAnimatePageChange() && newItemsScreenId != currentScreenId) {
                 // We post the animation slightly delayed to prevent slowdowns
                 // when we are loading right after we return to launcher.
-                mWorkspace.postDelayed(new Runnable() {
-                    public void run() {
-                        if (mWorkspace != null) {
-                            closeOpenViews(false);
-
-                            mWorkspace.snapToPage(newScreenIndex);
-                            mWorkspace.postDelayed(startBounceAnimRunnable,
-                                    NEW_APPS_ANIMATION_DELAY);
-                        }
-                    }
+                mWorkspace.postDelayed(() -> {
+                    closeOpenViews(false);
+                    mWorkspace.snapToPage(newScreenIndex);
+                    mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
                 }, NEW_APPS_PAGE_MOVE_DELAY);
             } else {
                 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
@@ -2284,36 +2225,13 @@
      * Add the views for a widget to the workspace.
      */
     public void bindAppWidget(LauncherAppWidgetInfo item) {
-        View view = inflateAppWidget(item);
+        View view = mItemInflater.inflateItem(item, getModelWriter());
         if (view != null) {
             mWorkspace.addInScreen(view, item);
             mWorkspace.requestLayout();
         }
     }
 
-    private View inflateAppWidget(LauncherAppWidgetInfo item) {
-        TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
-        try {
-            InflationResult inflationResult = mWidgetInflater.inflateAppWidget(item);
-            if (inflationResult.getType() == WidgetInflater.TYPE_DELETE) {
-                getModelWriter().deleteItemFromDatabase(item, inflationResult.getReason());
-                return null;
-            }
-
-            if (inflationResult.isUpdate()) {
-                getModelWriter().updateItemInDatabase(item);
-            }
-            AppWidgetHostView view = inflationResult.getType() == WidgetInflater.TYPE_PENDING
-                    ? new PendingAppWidgetHostView(this, item, inflationResult.getWidgetInfo())
-                    : mAppWidgetHolder.createView(
-                            item.appWidgetId, inflationResult.getWidgetInfo());
-            prepareAppWidget(view, item);
-            return view;
-        } finally {
-            TraceHelper.INSTANCE.endSection();
-        }
-    }
-
     /**
      * Restores a pending widget.
      *
@@ -3096,6 +3014,10 @@
         return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId);
     }
 
+    public ItemInflater<Launcher> getItemInflater() {
+        return mItemInflater;
+    }
+
     /**
      * Returns the current popup for testing, if any.
      */
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 1592154..7176733 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -2,11 +2,9 @@
 
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.view.ViewDebug;
 import android.view.WindowInsets;
@@ -112,15 +110,13 @@
         mSysUiScrim.setSize(r - l, b - t);
     }
 
-    @TargetApi(Build.VERSION_CODES.Q)
     public void setForceHideBackArrow(boolean forceHideBackArrow) {
         this.mForceHideBackArrow = forceHideBackArrow;
         setDisallowBackGesture(mDisallowBackGesture);
     }
 
-    @TargetApi(Build.VERSION_CODES.Q)
     public void setDisallowBackGesture(boolean disallowBackGesture) {
-        if (!Utilities.ATLEAST_Q || SEPARATE_RECENTS_ACTIVITY.get()) {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
         }
         mDisallowBackGesture = disallowBackGesture;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index e0f6101..2b886e4 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -121,15 +121,6 @@
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
 
-    @ChecksSdkIntAtLeast(api = VERSION_CODES.P)
-    public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
-
-    @ChecksSdkIntAtLeast(api = VERSION_CODES.Q)
-    public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
-
-    @ChecksSdkIntAtLeast(api = VERSION_CODES.R)
-    public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
-
     @ChecksSdkIntAtLeast(api = VERSION_CODES.S)
     public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index be4168d..2eff8aa 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -73,7 +73,6 @@
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.celllayout.CellInfo;
 import com.android.launcher3.celllayout.CellLayoutLayoutParams;
 import com.android.launcher3.celllayout.CellPosMapper;
@@ -99,7 +98,6 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemFactory;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.statemanager.StateManager;
@@ -2339,10 +2337,6 @@
         }
     }
 
-    public CellLayout getCurrentDragOverlappingLayout() {
-        return mDragOverlappingLayout;
-    }
-
     void setCurrentDropOverCell(int x, int y) {
         if (x != mDragOverX || y != mDragOverY) {
             mDragOverX = x;
@@ -2854,36 +2848,9 @@
         } else {
             // This is for other drag/drop cases, like dragging from All Apps
             mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
-            View view;
-
-            switch (info.itemType) {
-                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:
-                    if (info instanceof WorkspaceItemFactory) {
-                        // Came from all apps -- make a copy
-                        info = ((WorkspaceItemFactory) info).makeWorkspaceItem(mLauncher);
-                        d.dragInfo = info;
-                    }
-                    if (info instanceof WorkspaceItemInfo
-                            && info.container == LauncherSettings.Favorites.CONTAINER_PREDICTION) {
-                        // Came from all apps prediction row -- make a copy
-                        info = new WorkspaceItemInfo((WorkspaceItemInfo) info);
-                        d.dragInfo = info;
-                    }
-                    view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
-                    break;
-                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                    view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, mLauncher, cellLayout,
-                            (FolderInfo) info);
-                    break;
-                case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
-                    view = AppPairIcon.inflateIcon(R.layout.app_pair_icon, mLauncher, cellLayout,
-                            (FolderInfo) info);
-                    break;
-                default:
-                    throw new IllegalStateException("Unknown item type: " + info.itemType);
-            }
+            View view = mLauncher.getItemInflater()
+                    .inflateItem(info, mLauncher.getModelWriter(), cellLayout);
+            d.dragInfo = info = (ItemInfo) view.getTag();
 
             // First we find the cell nearest to point at which the item is
             // dropped, without any consideration to whether there is an item there.
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index ac5b528..0844275 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.keyboard.KeyboardDragAndDropView;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -126,7 +127,8 @@
     }
 
     private boolean supportAddToWorkSpace(ItemInfo item) {
-        return (item instanceof WorkspaceItemFactory)
+        return ((item instanceof AppInfo)
+                    && (((AppInfo) item).runtimeStatusFlags & FLAG_NOT_PINNABLE) == 0)
                 || ((item instanceof WorkspaceItemInfo)
                     && (((WorkspaceItemInfo) item).runtimeStatusFlags & FLAG_NOT_PINNABLE) == 0)
                 || ((item instanceof PendingAddItemInfo)
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 55438fe..930196d 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -126,6 +126,7 @@
     public static final float PULL_MULTIPLIER = .02f;
     public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
     protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
+    private static final int SCROLL_TO_BOTTOM_DURATION = 500;
     private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300;
     // Render the header protection at all times to debug clipping issues.
     private static final boolean DEBUG_HEADER_PROTECTION = false;
@@ -515,7 +516,7 @@
             // Switch to the main tab
             switchToTab(ActivityAllAppsContainerView.AdapterHolder.MAIN);
             // Scroll to bottom
-            getActiveRecyclerView().scrollToBottomWithMotion();
+            getActiveRecyclerView().scrollToBottomWithMotion(SCROLL_TO_BOTTOM_DURATION);
         });
     }
 
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index ad875e0..fba7537 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -35,6 +35,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.function.Predicate;
@@ -269,10 +270,10 @@
                 addApps = mWorkProviderManager.shouldShowWorkApps();
             }
             if (addApps) {
-                addAppsWithSections(mApps, position);
+                position = addAppsWithSections(mApps, position);
             }
             if (Flags.enablePrivateSpace()) {
-                addPrivateSpaceItems(position);
+                position = addPrivateSpaceItems(position);
             }
         }
         mAccessibilityResultsCount = (int) mAdapterItems.stream()
@@ -287,7 +288,8 @@
             for (AdapterItem item : mAdapterItems) {
                 item.rowIndex = 0;
                 if (BaseAllAppsAdapter.isDividerViewType(item.viewType)
-                        || BaseAllAppsAdapter.isPrivateSpaceHeaderView(item.viewType)) {
+                        || BaseAllAppsAdapter.isPrivateSpaceHeaderView(item.viewType)
+                        || BaseAllAppsAdapter.isPrivateSpaceSysAppsDividerView(item.viewType)) {
                     numAppsInSection = 0;
                 } else if (BaseAllAppsAdapter.isIconViewType(item.viewType)) {
                     if (numAppsInSection % mNumAppsPerRowAllApps == 0) {
@@ -309,12 +311,12 @@
         }
     }
 
-    void addPrivateSpaceItems(int position) {
+    int addPrivateSpaceItems(int position) {
         if (mPrivateProviderManager != null
                 && !mPrivateProviderManager.isPrivateSpaceHidden()
                 && !mPrivateApps.isEmpty()) {
             // Always add PS Header if Space is present and visible.
-            position += mPrivateProviderManager.addPrivateSpaceHeader(mAdapterItems);
+            position = mPrivateProviderManager.addPrivateSpaceHeader(mAdapterItems);
             int privateSpaceState = mPrivateProviderManager.getCurrentState();
             switch (privateSpaceState) {
                 case PrivateProfileManager.STATE_DISABLED:
@@ -322,15 +324,37 @@
                     break;
                 case PrivateProfileManager.STATE_ENABLED:
                     // Add PS Apps only in Enabled State.
-                    mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
-                    position++;
-                    addAppsWithSections(mPrivateApps, position);
+                    position = addPrivateSpaceApps(position);
                     break;
             }
         }
+        return position;
     }
 
-    private void addAppsWithSections(List<AppInfo> appList, int startPosition) {
+    private int addPrivateSpaceApps(int position) {
+        // Add Install Apps Button first.
+        if (Flags.privateSpaceAppInstallerButton()) {
+            mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
+            position++;
+        }
+
+        // Split of private space apps into user-installed and system apps.
+        Map<Boolean, List<AppInfo>> split = mPrivateApps.stream()
+                .collect(Collectors.partitioningBy(mPrivateProviderManager
+                                .splitIntoUserInstalledAndSystemApps()));
+        // Add user installed apps
+        position = addAppsWithSections(split.get(true), position);
+        // Add system apps separator.
+        if (Flags.privateSpaceSysAppsSeparation()) {
+            position = mPrivateProviderManager.addSystemAppsDivider(mAdapterItems);
+        }
+        // Add system apps.
+        position = addAppsWithSections(split.get(false), position);
+
+        return position;
+    }
+
+    private int addAppsWithSections(List<AppInfo> appList, int startPosition) {
         String lastSectionName = null;
         boolean hasPrivateApps = false;
         if (mPrivateProviderManager != null) {
@@ -357,6 +381,7 @@
             }
             startPosition++;
         }
+        return startPosition;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 5eeb259..28c87b6 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
+import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_LEFT;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_RIGHT;
 import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED;
@@ -61,7 +62,8 @@
     public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 4;
     public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 5;
     public static final int VIEW_TYPE_PRIVATE_SPACE_HEADER = 1 << 6;
-    public static final int NEXT_ID = 7;
+    public static final int VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER = 1 << 7;
+    public static final int NEXT_ID = 8;
 
     // Common view type masks
     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -69,6 +71,8 @@
 
     public static final int VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER =
             VIEW_TYPE_PRIVATE_SPACE_HEADER;
+    public static final int VIEW_TYPE_MASK_PRIVATE_SPACE_SYS_APPS_DIVIDER =
+            VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
 
     protected final SearchAdapterProvider<?> mAdapterProvider;
 
@@ -199,6 +203,11 @@
         return isViewType(viewType, VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER);
     }
 
+    /** Checks if the passed viewType represents private space system apps divider. */
+    public static boolean isPrivateSpaceSysAppsDividerView(int viewType) {
+        return isViewType(viewType, VIEW_TYPE_MASK_PRIVATE_SPACE_SYS_APPS_DIVIDER);
+    }
+
     public void setIconFocusListener(OnFocusChangeListener focusListener) {
         mIconFocusListener = focusListener;
     }
@@ -227,9 +236,9 @@
             case VIEW_TYPE_EMPTY_SEARCH:
                 return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
                         parent, false));
-            case VIEW_TYPE_ALL_APPS_DIVIDER:
+            case VIEW_TYPE_ALL_APPS_DIVIDER, VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
-                        R.layout.all_apps_divider, parent, false));
+                        R.layout.private_space_divider, parent, false));
             case VIEW_TYPE_WORK_EDU_CARD:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.work_apps_edu, parent, false));
@@ -282,6 +291,11 @@
                         new SectionDecorationInfo(mActivityContext, roundRegions,
                                 false /* decorateTogether */);
                 break;
+            case VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER:
+                adapterItem = mApps.getAdapterItems().get(position);
+                adapterItem.decorationInfo = new SectionDecorationInfo(mActivityContext,
+                        ROUND_NOTHING, true /* decorateTogether */);
+                break;
             case VIEW_TYPE_ALL_APPS_DIVIDER:
             case VIEW_TYPE_WORK_DISABLED_CARD:
                 // nothing to do
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 5e48177..63a168e 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -21,7 +21,6 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.statemanager.StateManager;
 
 /**
@@ -43,11 +42,7 @@
 
     @Override
     protected int computeNavBarScrimHeight(WindowInsets insets) {
-        if (Utilities.ATLEAST_Q) {
-            return insets.getTappableElementInsets().bottom;
-        } else {
-            return insets.getStableInsetBottom();
-        }
+        return insets.getTappableElementInsets().bottom;
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 1c46dac..557ec48 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP;
@@ -45,10 +46,11 @@
 import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SettingsCache;
-import com.android.launcher3.util.UserIconInfo;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Predicate;
 
 /**
@@ -62,6 +64,8 @@
     private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal";
     private final ActivityAllAppsContainerView<?> mAllApps;
     private final Predicate<UserHandle> mPrivateProfileMatcher;
+    private Set<String> mPreInstalledSystemPackages = new HashSet<>();
+    private Intent mAppInstallerIntent = new Intent();
     private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator;
     private boolean mPrivateSpaceSettingsAvailable;
     private Runnable mUnlockRunnable;
@@ -73,7 +77,7 @@
         super(userManager, statsLogManager, userCache);
         mAllApps = allApps;
         mPrivateProfileMatcher = (user) -> userCache.getUserInfo(user).isPrivate();
-        UI_HELPER_EXECUTOR.post(this::setPrivateSpaceSettingsAvailable);
+        UI_HELPER_EXECUTOR.post(this::initializeInBackgroundThread);
     }
 
     /** Adds Private Space Header to the layout. */
@@ -83,18 +87,17 @@
         return adapterItems.size();
     }
 
+    /** Adds Private Space System Apps Divider to the layout. */
+    public int addSystemAppsDivider(List<BaseAllAppsAdapter.AdapterItem> adapterItems) {
+        adapterItems.add(new BaseAllAppsAdapter
+                .AdapterItem(VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER));
+        mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
+        return adapterItems.size();
+    }
+
     /** Adds Private Space install app button to the layout. */
     public void addPrivateSpaceInstallAppButton(List<BaseAllAppsAdapter.AdapterItem> adapterItems) {
         Context context = mAllApps.getContext();
-        // Prepare intent
-        UserCache userCache = UserCache.getInstance(context);
-        UserHandle userHandle = userCache.getUserProfiles().stream()
-                .filter(user -> userCache.getUserInfo(user).type == UserIconInfo.TYPE_PRIVATE)
-                .findFirst()
-                .orElse(null);
-        Intent intent = ApiWrapper.getAppMarketActivityIntent(context,
-                BuildConfig.APPLICATION_ID, userHandle);
-
         // Prepare bitmapInfo
         Intent.ShortcutIconResource shortcut = Intent.ShortcutIconResource.fromContext(
                 context, com.android.launcher3.R.drawable.private_space_install_app_icon);
@@ -102,7 +105,7 @@
 
         AppInfo itemInfo = new AppInfo();
         itemInfo.title = context.getResources().getString(R.string.ps_add_button_label);
-        itemInfo.intent = intent;
+        itemInfo.intent = mAppInstallerIntent;
         itemInfo.bitmap = bitmapInfo;
         itemInfo.contentDescription = context.getResources().getString(
                 com.android.launcher3.R.string.ps_add_button_content_description);
@@ -166,6 +169,22 @@
         return mPrivateSpaceSettingsAvailable;
     }
 
+    /** Initializes binder call based properties in non-main thread.
+     * <p>
+     * This can cause the Private Space container items to not load/respond correctly sometimes,
+     * when the All Apps Container loads for the first time (device restarts, new profiles
+     * added/removed, etc.), as the properties are being set in non-ui thread whereas the container
+     * loads in the ui thread.
+     * This case should still be ok, as locking the Private Space container and unlocking it,
+     * reloads the values, fixing the incorrect UI.
+     */
+    private void initializeInBackgroundThread() {
+        Preconditions.assertNonUiThread();
+        setPreInstalledSystemPackages();
+        setAppInstallerIntent();
+        setPrivateSpaceSettingsAvailable();
+    }
+
     private void setPrivateSpaceSettingsAvailable() {
         if (mPrivateSpaceSettingsAvailable) {
             return;
@@ -178,6 +197,22 @@
         mPrivateSpaceSettingsAvailable = resolveInfo != null;
     }
 
+    private void setPreInstalledSystemPackages() {
+        Preconditions.assertNonUiThread();
+        if (getProfileUser() != null) {
+            mPreInstalledSystemPackages = new HashSet<>(ApiWrapper
+                    .getPreInstalledSystemPackages(mAllApps.getContext(), getProfileUser()));
+        }
+    }
+
+    private void setAppInstallerIntent() {
+        Preconditions.assertNonUiThread();
+        if (getProfileUser() != null) {
+            mAppInstallerIntent = ApiWrapper.getAppMarketActivityIntent(mAllApps.getContext(),
+                    BuildConfig.APPLICATION_ID, getProfileUser());
+        }
+    }
+
     @VisibleForTesting
     void resetPrivateSpaceDecorator(int updatedState) {
         ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN);
@@ -225,4 +260,14 @@
     public Predicate<UserHandle> getUserMatcher() {
         return mPrivateProfileMatcher;
     }
+
+    /**
+     * Splits private apps into user installed and system apps.
+     * When the list of system apps is empty, all apps are treated as system.
+     */
+    public Predicate<AppInfo> splitIntoUserInstalledAndSystemApps() {
+        return appInfo -> !mPreInstalledSystemPackages.isEmpty()
+                && (appInfo.componentName == null
+                || !(mPreInstalledSystemPackages.contains(appInfo.componentName.getPackageName())));
+    }
 }
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index bc3269d..bc55597 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -120,11 +120,7 @@
                 (ActivityAllAppsContainerView<?>.AdapterHolder) mAllApps.mAH.get(MAIN);
         if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
                 && mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
-            RecyclerViewAnimationController recyclerViewAnimationController =
-                    new RecyclerViewAnimationController(mAllApps);
-            recyclerViewAnimationController.animateToState(true /* expand */,
-                    ANIMATION_DURATION, () -> {});
-            mAllApps.getActiveRecyclerView().scrollToBottomWithMotion();
+            mAllApps.getActiveRecyclerView().scrollToBottomWithMotion(ANIMATION_DURATION);
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 8894f45..6a1f37a 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -23,7 +23,6 @@
 
 import androidx.annotation.IntDef;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
@@ -70,16 +69,13 @@
 
     /** Sets quiet mode as enabled/disabled for the profile type. */
     protected void setQuietMode(boolean enabled) {
-        if (Utilities.ATLEAST_P) {
-            UI_HELPER_EXECUTOR.post(() -> {
+        UI_HELPER_EXECUTOR.post(() ->
                 mUserCache.getUserProfiles()
                         .stream()
                         .filter(getUserMatcher())
                         .findFirst()
                         .ifPresent(userHandle ->
-                                mUserManager.requestQuietModeEnabled(enabled, userHandle));
-            });
-        }
+                                mUserManager.requestQuietModeEnabled(enabled, userHandle)));
     }
 
     /** Sets current state for the profile type. */
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 48400b2..eb7d429 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -83,11 +83,9 @@
         mIcon = findViewById(R.id.work_icon);
         mTextView = findViewById(R.id.pause_text);
         setSelected(true);
-        if (Utilities.ATLEAST_R) {
-            KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
-                    new KeyboardInsetAnimationCallback(this);
-            setWindowInsetsAnimationCallback(keyboardInsetAnimationCallback);
-        }
+        KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
+                new KeyboardInsetAnimationCallback(this);
+        setWindowInsetsAnimationCallback(keyboardInsetAnimationCallback);
 
         setInsets(mActivityContext.getDeviceProfile().getInsets());
         updateStringFromCache();
diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java
index 1882667..e1eeabe 100644
--- a/src/com/android/launcher3/allapps/WorkPausedCard.java
+++ b/src/com/android/launcher3/allapps/WorkPausedCard.java
@@ -26,7 +26,6 @@
 import android.widget.TextView;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.views.ActivityContext;
 
@@ -80,11 +79,9 @@
 
     @Override
     public void onClick(View view) {
-        if (Utilities.ATLEAST_P) {
-            setEnabled(false);
-            mActivityContext.getAppsView().getWorkManager().setWorkProfileEnabled(true);
-            mActivityContext.getStatsLogManager().logger().log(LAUNCHER_TURN_ON_WORK_APPS_TAP);
-        }
+        setEnabled(false);
+        mActivityContext.getAppsView().getWorkManager().setWorkProfileEnabled(true);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_TURN_ON_WORK_APPS_TAP);
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index c430a36..a54e52c 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -199,8 +199,7 @@
     }
 
     private void onWorkFabClicked(View view) {
-        if (Utilities.ATLEAST_P && getCurrentState() == STATE_ENABLED
-                && mWorkModeSwitch.isEnabled()) {
+        if (getCurrentState() == STATE_ENABLED && mWorkModeSwitch.isEnabled()) {
             logEvents(LAUNCHER_TURN_OFF_WORK_APPS_TAP);
             setWorkProfileEnabled(false);
         }
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index fd731f4..586beb2 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -25,7 +25,6 @@
 import android.os.Trace;
 import android.util.FloatProperty;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
 
 import java.util.ArrayList;
@@ -86,7 +85,7 @@
 
     /** If trace is enabled, add counter to trace animation progress. */
     public void logAnimationProgressToTrace(String counterName) {
-        if (Utilities.ATLEAST_Q && Trace.isEnabled()) {
+        if (Trace.isEnabled()) {
             super.addOnFrameListener(
                     animation -> Trace.setCounter(
                             counterName, (long) (animation.getAnimatedFraction() * 100)));
diff --git a/src/com/android/launcher3/celllayout/ReorderPreviewAnimation.kt b/src/com/android/launcher3/celllayout/ReorderPreviewAnimation.kt
new file mode 100644
index 0000000..62b19d4
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/ReorderPreviewAnimation.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 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.celllayout
+
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.animation.ValueAnimator.areAnimatorsEnabled
+import android.util.ArrayMap
+import android.view.View
+import com.android.app.animation.Interpolators.DECELERATE_1_5
+import com.android.launcher3.CellLayout
+import com.android.launcher3.CellLayout.REORDER_ANIMATION_DURATION
+import com.android.launcher3.Reorderable
+import com.android.launcher3.Workspace
+import com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_BOUNCE_OFFSET
+import com.android.launcher3.util.Thunk
+import kotlin.math.abs
+import kotlin.math.atan
+import kotlin.math.cos
+import kotlin.math.sign
+import kotlin.math.sin
+
+/**
+ * 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<T>(
+    val child: T,
+    // If the mode is MODE_HINT it will only move one period and stop, it then will be going
+    // backwards to the initial position, otherwise it will oscillate.
+    val mode: Int,
+    cellX0: Int,
+    cellY0: Int,
+    cellX1: Int,
+    cellY1: Int,
+    spanX: Int,
+    spanY: Int,
+    reorderMagnitude: Float,
+    cellLayout: CellLayout,
+    private val shakeAnimators: ArrayMap<Reorderable, ReorderPreviewAnimation<T>>
+) : ValueAnimator.AnimatorUpdateListener where T : View, T : Reorderable {
+
+    private var finalDeltaX = 0f
+    private var finalDeltaY = 0f
+    private var initDeltaX =
+        child.getTranslateDelegate().getTranslationX(INDEX_REORDER_BOUNCE_OFFSET).value
+    private var initDeltaY =
+        child.getTranslateDelegate().getTranslationY(INDEX_REORDER_BOUNCE_OFFSET).value
+    private var initScale = child.getReorderBounceScale()
+    private val finalScale = CellLayout.DEFAULT_SCALE - CHILD_DIVIDEND / child.width * initScale
+
+    private val dir = if (mode == MODE_HINT) -1 else 1
+    var animator: ValueAnimator =
+        ObjectAnimator.ofFloat(0f, 1f).also {
+            it.addUpdateListener(this)
+            it.setDuration((if (mode == MODE_HINT) HINT_DURATION else PREVIEW_DURATION).toLong())
+            it.startDelay = (Math.random() * 60).toLong()
+            // Animations are disabled in power save mode, causing the repeated animation to jump
+            // spastically between beginning and end states. Since this looks bad, we don't repeat
+            // the animation in power save mode.
+            if (areAnimatorsEnabled() && mode == MODE_PREVIEW) {
+                it.repeatCount = ValueAnimator.INFINITE
+                it.repeatMode = ValueAnimator.REVERSE
+            }
+        }
+
+    init {
+        val tmpRes = intArrayOf(0, 0)
+        cellLayout.regionToCenterPoint(cellX0, cellY0, spanX, spanY, tmpRes)
+        val (x0, y0) = tmpRes
+        cellLayout.regionToCenterPoint(cellX1, cellY1, spanX, spanY, tmpRes)
+        val (x1, y1) = tmpRes
+        val dX = x1 - x0
+        val dY = y1 - y0
+
+        if (dX != 0 || dY != 0) {
+            if (dY == 0) {
+                finalDeltaX = -dir * sign(dX.toFloat()) * reorderMagnitude
+            } else if (dX == 0) {
+                finalDeltaY = -dir * sign(dY.toFloat()) * reorderMagnitude
+            } else {
+                val angle = atan((dY.toFloat() / dX))
+                finalDeltaX = (-dir * sign(dX.toFloat()) * abs(cos(angle) * reorderMagnitude))
+                finalDeltaY = (-dir * sign(dY.toFloat()) * abs(sin(angle) * reorderMagnitude))
+            }
+        }
+    }
+
+    private fun setInitialAnimationValuesToBaseline() {
+        initScale = CellLayout.DEFAULT_SCALE
+        initDeltaX = 0f
+        initDeltaY = 0f
+    }
+
+    fun animate() {
+        val noMovement = finalDeltaX == 0f && finalDeltaY == 0f
+        if (shakeAnimators.containsKey(child)) {
+            val oldAnimation: ReorderPreviewAnimation<T>? = shakeAnimators.remove(child)
+            if (noMovement) {
+                // A previous animation for this item exists, and no new animation will exist.
+                // Finish the old animation smoothly.
+                oldAnimation!!.finishAnimation()
+                return
+            } else {
+                // A previous animation for this item exists, and a new one will exist. Stop
+                // the old animation in its tracks, and proceed with the new one.
+                oldAnimation!!.cancel()
+            }
+        }
+        if (noMovement) {
+            return
+        }
+        shakeAnimators[child] = this
+        animator.start()
+    }
+
+    override fun onAnimationUpdate(updatedAnimation: ValueAnimator) {
+        val progress = updatedAnimation.animatedValue as Float
+        child
+            .getTranslateDelegate()
+            .setTranslation(
+                INDEX_REORDER_BOUNCE_OFFSET,
+                /* x = */ progress * finalDeltaX + (1 - progress) * initDeltaX,
+                /* y = */ progress * finalDeltaY + (1 - progress) * initDeltaY
+            )
+        child.setReorderBounceScale(progress * finalScale + (1 - progress) * initScale)
+    }
+
+    private fun cancel() {
+        animator.cancel()
+    }
+
+    /** Smoothly returns the item to its baseline position / scale */
+    @Thunk
+    fun finishAnimation() {
+        animator.cancel()
+        setInitialAnimationValuesToBaseline()
+        animator = ObjectAnimator.ofFloat((animator.animatedValue as Float), 0f)
+        animator.addUpdateListener(this)
+        animator.interpolator = DECELERATE_1_5
+        animator.setDuration(REORDER_ANIMATION_DURATION.toLong())
+        animator.start()
+    }
+
+    companion object {
+        const val PREVIEW_DURATION = 300
+        const val HINT_DURATION = Workspace.REORDER_TIMEOUT
+        private const val CHILD_DIVIDEND = 4.0f
+        const val MODE_HINT = 0
+        const val MODE_PREVIEW = 1
+    }
+}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index d37b1f0..5f786a4 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -110,9 +110,6 @@
     }
 
     public static int getRecommendedTimeoutMillis(Context context, int originalTimeout, int flags) {
-        if (Utilities.ATLEAST_Q) {
-            return getManager(context).getRecommendedTimeoutMillis(originalTimeout, flags);
-        }
-        return originalTimeout;
+        return getManager(context).getRecommendedTimeoutMillis(originalTimeout, flags);
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index aa5329b..3ccde0a 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.Utilities.ATLEAST_Q;
-
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -404,9 +402,7 @@
             mMotionDown.set(dragLayerPos.x,  dragLayerPos.y);
         }
 
-        if (ATLEAST_Q) {
-            mLastTouchClassification = ev.getClassification();
-        }
+        mLastTouchClassification = ev.getClassification();
         return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
     }
 
@@ -441,7 +437,7 @@
         mLastTouch.set(x, y);
 
         int distanceDragged = mDistanceSinceScroll;
-        if (ATLEAST_Q && mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
+        if (mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
             distanceDragged /= DEEP_PRESS_DISTANCE_FACTOR;
         }
         if (mIsInPreDrag && mOptions.preDragCondition != null
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 6f295e6..6a43b24 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -37,7 +37,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.icons.BitmapRenderer;
@@ -74,13 +73,9 @@
         return mBadge;
     }
 
-    @TargetApi(Build.VERSION_CODES.P)
     public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
             ActivityContext activity, int folderId, Point size) {
         Preconditions.assertNonUiThread();
-        if (!Utilities.ATLEAST_P) {
-            return null;
-        }
 
         // assume square
         if (size.x != size.y) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 084f829..7ae70e0 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -297,10 +297,8 @@
         mFooter = findViewById(R.id.folder_footer);
         mFooterHeight = dp.folderFooterHeightPx;
 
-        if (Utilities.ATLEAST_R) {
-            mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
-            setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
-        }
+        mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
+        setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
     }
 
     public boolean onLongClick(View v) {
@@ -422,18 +420,16 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
-        if (Utilities.ATLEAST_R) {
-            this.setTranslationY(0);
+        this.setTranslationY(0);
 
-            if (windowInsets.isVisible(WindowInsets.Type.ime())) {
-                Insets keyboardInsets = windowInsets.getInsets(WindowInsets.Type.ime());
-                int folderHeightFromBottom = getHeightFromBottom();
+        if (windowInsets.isVisible(WindowInsets.Type.ime())) {
+            Insets keyboardInsets = windowInsets.getInsets(WindowInsets.Type.ime());
+            int folderHeightFromBottom = getHeightFromBottom();
 
-                if (keyboardInsets.bottom > folderHeightFromBottom) {
-                    // Translate this folder above the keyboard, then add the folder name's padding
-                    this.setTranslationY(folderHeightFromBottom - keyboardInsets.bottom
-                            - mFolderName.getPaddingBottom());
-                }
+            if (keyboardInsets.bottom > folderHeightFromBottom) {
+                // Translate this folder above the keyboard, then add the folder name's padding
+                this.setTranslationY(folderHeightFromBottom - keyboardInsets.bottom
+                        - mFolderName.getPaddingBottom());
             }
         }
 
@@ -812,15 +808,13 @@
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                if (Utilities.ATLEAST_R) {
-                    setWindowInsetsAnimationCallback(null);
-                }
+                setWindowInsetsAnimationCallback(null);
                 mIsAnimatingClosed = true;
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (Utilities.ATLEAST_R && mKeyboardInsetAnimationCallback != null) {
+                if (mKeyboardInsetAnimationCallback != null) {
                     setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
                 }
                 closeComplete(true);
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index 66c9109..78298b3 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -94,7 +94,8 @@
                         CellLayout cellLayout = mLauncher.getCellLayout(info.container,
                                 mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId);
                         finalItem =  info.contents.remove(0);
-                        newIcon = mLauncher.createShortcut(cellLayout, finalItem);
+                        newIcon = mLauncher.getItemInflater().inflateItem(
+                                finalItem, mLauncher.getModelWriter(), cellLayout);
                         mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
                                 info.container, info.screenId, info.cellX, info.cellY);
                     }
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 18200f6..dc8694d 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -19,7 +19,6 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.Themes.isThemedIconEnabled;
 
-import android.annotation.TargetApi;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.pm.PackageManager;
@@ -27,7 +26,6 @@
 import android.database.MatrixCursor;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -41,7 +39,6 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.GridOption;
 import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Executors;
 
 /**
@@ -184,13 +181,12 @@
             return null;
         }
 
-        if (!Utilities.ATLEAST_R || !METHOD_GET_PREVIEW.equals(method)) {
+        if (!METHOD_GET_PREVIEW.equals(method)) {
             return null;
         }
         return getPreview(extras);
     }
 
-    @TargetApi(Build.VERSION_CODES.R)
     private synchronized Bundle getPreview(Bundle request) {
         PreviewLifecycleObserver observer = null;
         try {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 7dcc8a8..e0a6627 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
 import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
 
-import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
@@ -39,7 +38,6 @@
 import android.content.res.TypedArray;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.AttributeSet;
@@ -121,7 +119,6 @@
  *   3) Place appropriate elements like icons and first-page qsb
  *   4) Measure and draw the view on a canvas
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class LauncherPreviewRenderer extends ContextWrapper
         implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
 
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index ec6b94d..051fb6f 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -49,7 +49,6 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
 import com.android.launcher3.model.BgDataModel;
@@ -211,10 +210,7 @@
             return new ContextThemeWrapper(context,
                     Themes.getActivityThemeRes(context));
         }
-        if (Utilities.ATLEAST_R) {
-            context = context.createWindowContext(
-                    LayoutParams.TYPE_APPLICATION_OVERLAY, null);
-        }
+        context = context.createWindowContext(LayoutParams.TYPE_APPLICATION_OVERLAY, null);
         LocalColorExtractor.newInstance(context)
                 .applyColorsOverride(context, mWallpaperColors);
         return new ContextThemeWrapper(context,
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1044dfb..2f678a8 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -50,11 +50,13 @@
 import com.android.launcher3.model.data.IconRequestInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.UserIconInfo;
 
 import java.net.URISyntaxException;
 import java.security.InvalidParameterException;
@@ -353,6 +355,8 @@
         final WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.user = user;
         info.intent = newIntent;
+        UserCache userCache = UserCache.getInstance(mContext);
+        UserIconInfo userIconInfo = userCache.getUserInfo(user);
 
         if (loadIcon) {
             mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
@@ -362,7 +366,7 @@
         }
 
         if (mActivityInfo != null) {
-            AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo);
+            AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo, userIconInfo);
         }
 
         // from the db
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d0a1f10..736b80a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -655,7 +655,7 @@
             // Create the ApplicationInfos
             for (int i = 0; i < apps.size(); i++) {
                 LauncherActivityInfo app = apps.get(i);
-                AppInfo appInfo = new AppInfo(app, user, quietMode);
+                AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user), quietMode);
                 if (enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
                     // For archived apps, include progress info in case there is a pending
                     // install session post restart of device.
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 6c64713..ba2b64d 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -458,7 +458,7 @@
             LauncherWidgetHolder widgetHolder) {
         ContentResolver cr = mContext.getContentResolver();
         String blobHandlerDigest = Settings.Secure.getString(cr, LAYOUT_DIGEST_KEY);
-        if (Utilities.ATLEAST_R && !TextUtils.isEmpty(blobHandlerDigest)) {
+        if (!TextUtils.isEmpty(blobHandlerDigest)) {
             BlobStoreManager blobManager = mContext.getSystemService(BlobStoreManager.class);
             try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
                     blobManager.openBlob(BlobHandle.createWithSha256(
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index ea8a7a1..72eda6c 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -31,10 +31,13 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.UserIconInfo;
 
 import java.util.Comparator;
 
@@ -83,20 +86,21 @@
      * Must not hold the Context.
      */
     public AppInfo(Context context, LauncherActivityInfo info, UserHandle user) {
-        this(info, user, context.getSystemService(UserManager.class).isQuietModeEnabled(user));
+        this(info, UserCache.INSTANCE.get(context).getUserInfo(user),
+                context.getSystemService(UserManager.class).isQuietModeEnabled(user));
     }
 
-    public AppInfo(LauncherActivityInfo info, UserHandle user, boolean quietModeEnabled) {
+    public AppInfo(LauncherActivityInfo info, UserIconInfo userIconInfo, boolean quietModeEnabled) {
         this.componentName = info.getComponentName();
         this.container = CONTAINER_ALL_APPS;
-        this.user = user;
+        this.user = userIconInfo.user;
         intent = makeLaunchIntent(info);
 
         if (quietModeEnabled) {
             runtimeStatusFlags |= FLAG_DISABLED_QUIET_USER;
         }
         uid = info.getApplicationInfo().uid;
-        updateRuntimeFlagsForActivityTarget(this, info);
+        updateRuntimeFlagsForActivityTarget(this, info, userIconInfo);
     }
 
     public AppInfo(AppInfo info) {
@@ -170,7 +174,7 @@
     }
 
     public static void updateRuntimeFlagsForActivityTarget(
-            ItemInfoWithIcon info, LauncherActivityInfo lai) {
+            ItemInfoWithIcon info, LauncherActivityInfo lai, UserIconInfo userIconInfo) {
         ApplicationInfo appInfo = lai.getApplicationInfo();
         if (PackageManagerHelper.isAppSuspended(appInfo)) {
             info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
@@ -181,6 +185,12 @@
         info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
                 ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
 
+        if (Flags.privateSpaceRestrictAccessibilityDrag()) {
+            if (userIconInfo.isPrivate()) {
+                info.runtimeStatusFlags |= FLAG_NOT_PINNABLE;
+            }
+        }
+
         // Sets the progress level, installation and incremental download flags.
         info.setProgressLevel(
                 PackageManagerHelper.getLoadingProgress(lai),
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index c67ec5a..435d223 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -25,10 +25,12 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.ContentWriter;
@@ -120,6 +122,11 @@
     public WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context) {
         user = shortcutInfo.getUserHandle();
         itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+        if (Flags.privateSpaceRestrictAccessibilityDrag()) {
+            if (UserCache.INSTANCE.get(context).getUserInfo(user).isPrivate()) {
+                runtimeStatusFlags |= FLAG_NOT_PINNABLE;
+            }
+        }
         updateFromDeepShortcutInfo(shortcutInfo, context);
     }
 
@@ -182,8 +189,7 @@
             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
         }
         disabledMessage = shortcutInfo.getDisabledMessage();
-        if (Utilities.ATLEAST_P
-                && shortcutInfo.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
+        if (shortcutInfo.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
             runtimeStatusFlags |= FLAG_DISABLED_VERSION_LOWER;
         } else {
             runtimeStatusFlags &= ~FLAG_DISABLED_VERSION_LOWER;
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index ca27eb2..0d47462 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -24,7 +24,6 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
-import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 
@@ -34,7 +33,6 @@
 
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.util.IntArray;
@@ -132,7 +130,7 @@
     public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
         for (SessionInfo info : getAllVerifiedSessions()) {
             boolean match = pkg.equals(info.getAppPackageName());
-            if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
+            if (!user.equals(getUserHandle(info))) {
                 match = false;
             }
             if (match) {
@@ -180,9 +178,8 @@
 
     @NonNull
     public List<SessionInfo> getAllVerifiedSessions() {
-        List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
-                ? Objects.requireNonNull(mLauncherApps).getAllPackageInstallerSessions()
-                : mInstaller.getAllSessions());
+        List<SessionInfo> list = new ArrayList<>(
+                Objects.requireNonNull(mLauncherApps).getAllPackageInstallerSessions());
         Iterator<SessionInfo> it = list.iterator();
         while (it.hasNext()) {
             if (verify(it.next()) == null) {
@@ -252,6 +249,6 @@
     }
 
     public static UserHandle getUserHandle(@NonNull final SessionInfo info) {
-        return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+        return info.getUser();
     }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 934d43b..c9c5fd3 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.popup;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
-import static com.android.launcher3.Utilities.ATLEAST_P;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
@@ -248,10 +247,7 @@
      * Animates and loads shortcuts on background thread for this popup container
      */
     private void loadAppShortcuts(ItemInfo originalItemInfo) {
-
-        if (ATLEAST_P) {
-            setAccessibilityPaneTitle(getTitleForAccessibility());
-        }
+        setAccessibilityPaneTitle(getTitleForAccessibility());
         mOriginalIcon.setForceHideDot(true);
         // All views are added. Animate layout from now on.
         setLayoutTransition(new LayoutTransition());
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 8df58d2..688da49 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -20,13 +20,11 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.annotation.TargetApi;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Build;
 import android.util.Log;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -42,7 +40,6 @@
 
 import java.lang.ref.WeakReference;
 
-@TargetApi(Build.VERSION_CODES.Q)
 public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
     private static final String TAG = "RemoteActionShortcut";
     private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 1c53855..22bc13b 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -326,9 +326,6 @@
      */
     private UserHandle getUserForAncestralSerialNumber(BackupManager backupManager,
             long ancestralSerialNumber) {
-        if (!Utilities.ATLEAST_Q) {
-            return null;
-        }
         return backupManager.getUserForAncestralSerialNumber(ancestralSerialNumber);
     }
 
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 8cb15a5..a5f9c2a 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -52,7 +52,6 @@
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.uioverrides.flags.DeveloperOptionsUI;
@@ -120,7 +119,7 @@
     }
 
     private boolean startPreference(String fragment, Bundle args, String key) {
-        if (Utilities.ATLEAST_P && getSupportFragmentManager().isStateSaved()) {
+        if (getSupportFragmentManager().isStateSaved()) {
             // Sometimes onClick can come after onPause because of being posted on the handler.
             // Skip starting new preferences in that case.
             return false;
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index ccff095..315b5e3 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -15,19 +15,17 @@
  */
 package com.android.launcher3.testing;
 
-import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
 import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.Build;
 import android.os.Bundle;
 import android.view.WindowInsets;
 
@@ -60,7 +58,6 @@
 /**
  * Class to handle requests from tests
  */
-@TargetApi(Build.VERSION_CODES.Q)
 public class TestInformationHandler implements ResourceBasedOverride {
 
     public static TestInformationHandler newInstance(Context context) {
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 369008d..9d50e6a 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -190,16 +190,12 @@
             boolean downloadStarted) {
         ItemInfo item = (ItemInfo) v.getTag();
         CompletableFuture<SessionInfo> siFuture;
-        if (Utilities.ATLEAST_Q) {
-            siFuture = CompletableFuture.supplyAsync(() ->
-                    InstallSessionHelper.INSTANCE.get(launcher)
-                            .getActiveSessionInfo(item.user, packageName),
-                    UI_HELPER_EXECUTOR);
-        } else {
-            siFuture = CompletableFuture.completedFuture(null);
-        }
+        siFuture = CompletableFuture.supplyAsync(() ->
+                        InstallSessionHelper.INSTANCE.get(launcher)
+                                .getActiveSessionInfo(item.user, packageName),
+                UI_HELPER_EXECUTOR);
         Consumer<SessionInfo> marketLaunchAction = sessionInfo -> {
-            if (sessionInfo != null && Utilities.ATLEAST_Q) {
+            if (sessionInfo != null) {
                 LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
                 try {
                     launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index 1cec0ec..296efe9 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -19,7 +19,6 @@
 import android.os.FileUtils;
 import android.util.Log;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 
 import java.io.ByteArrayOutputStream;
@@ -51,17 +50,7 @@
     }
 
     public static long copy(InputStream from, OutputStream to) throws IOException {
-        if (Utilities.ATLEAST_Q) {
-            return FileUtils.copy(from, to);
-        }
-        byte[] buf = new byte[BUF_SIZE];
-        long total = 0;
-        int r;
-        while ((r = from.read(buf)) != -1) {
-            to.write(buf, 0, r);
-            total += r;
-        }
-        return total;
+        return FileUtils.copy(from, to);
     }
 
     public static void closeSilently(Closeable c) {
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
new file mode 100644
index 0000000..79091ca
--- /dev/null
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 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 android.appwidget.AppWidgetHostView
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.OnClickListener
+import android.view.View.OnFocusChangeListener
+import android.view.ViewGroup
+import com.android.launcher3.BubbleTextView
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.R
+import com.android.launcher3.apppairs.AppPairIcon
+import com.android.launcher3.folder.FolderIcon
+import com.android.launcher3.model.ModelWriter
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.model.data.WorkspaceItemFactory
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.views.ActivityContext
+import com.android.launcher3.widget.LauncherWidgetHolder
+import com.android.launcher3.widget.PendingAppWidgetHostView
+import com.android.launcher3.widget.WidgetInflater
+
+/** Utility class to inflate View for a model item */
+class ItemInflater<T>(
+    private val context: T,
+    private val widgetHolder: LauncherWidgetHolder,
+    private val clickListener: OnClickListener,
+    private val focusListener: OnFocusChangeListener,
+    private val defaultParent: ViewGroup
+) where T : Context, T : ActivityContext {
+
+    private val widgetInflater = WidgetInflater(context)
+
+    @JvmOverloads
+    fun inflateItem(item: ItemInfo, writer: ModelWriter, nullableParent: ViewGroup? = null): View? {
+        val parent = nullableParent ?: defaultParent
+        when (item.itemType) {
+            Favorites.ITEM_TYPE_APPLICATION,
+            Favorites.ITEM_TYPE_DEEP_SHORTCUT,
+            Favorites.ITEM_TYPE_SEARCH_ACTION -> {
+                var info =
+                    if (item is WorkspaceItemFactory) {
+                        (item as WorkspaceItemFactory).makeWorkspaceItem(context)
+                    } else {
+                        item as WorkspaceItemInfo
+                    }
+                if (info.container == Favorites.CONTAINER_PREDICTION) {
+                    // Came from all apps prediction row -- make a copy
+                    info = WorkspaceItemInfo(info)
+                }
+                return createShortcut(info, parent)
+            }
+            Favorites.ITEM_TYPE_FOLDER ->
+                return FolderIcon.inflateFolderAndIcon(
+                    R.layout.folder_icon,
+                    context,
+                    parent,
+                    item as FolderInfo
+                )
+            Favorites.ITEM_TYPE_APP_PAIR ->
+                return AppPairIcon.inflateIcon(
+                    R.layout.app_pair_icon,
+                    context,
+                    parent,
+                    item as FolderInfo
+                )
+            Favorites.ITEM_TYPE_APPWIDGET,
+            Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
+                return inflateAppWidget(item as LauncherAppWidgetInfo, writer)
+            else -> throw RuntimeException("Invalid Item Type")
+        }
+    }
+
+    /**
+     * Creates a view representing a shortcut inflated from the specified resource.
+     *
+     * @param parent The group the shortcut belongs to. This is not necessarily the group where the
+     *   shortcut should be added.
+     * @param info The data structure describing the shortcut.
+     * @return A View inflated from layoutResId.
+     */
+    private fun createShortcut(info: WorkspaceItemInfo, parent: ViewGroup): View {
+        val favorite =
+            LayoutInflater.from(parent.context).inflate(R.layout.app_icon, parent, false)
+                as BubbleTextView
+        favorite.applyFromWorkspaceItem(info)
+        favorite.setOnClickListener(clickListener)
+        favorite.onFocusChangeListener = focusListener
+        return favorite
+    }
+
+    private fun inflateAppWidget(item: LauncherAppWidgetInfo, writer: ModelWriter): View? {
+        TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId)
+        try {
+            val (type, reason, _, isUpdate, widgetInfo) = widgetInflater.inflateAppWidget(item)
+            if (type == WidgetInflater.TYPE_DELETE) {
+                writer.deleteItemFromDatabase(item, reason)
+                return null
+            }
+            if (isUpdate) {
+                writer.updateItemInDatabase(item)
+            }
+            val view =
+                if (type == WidgetInflater.TYPE_PENDING || widgetInfo == null)
+                    PendingAppWidgetHostView(context, item, widgetInfo)
+                else widgetHolder.createView(item.appWidgetId, widgetInfo)
+            prepareAppWidget(view, item)
+            return view
+        } finally {
+            TraceHelper.INSTANCE.endSection()
+        }
+    }
+
+    fun prepareAppWidget(hostView: AppWidgetHostView, item: LauncherAppWidgetInfo) {
+        hostView.tag = item
+        hostView.isFocusable = true
+        hostView.onFocusChangeListener = focusListener
+    }
+}
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index 138cc4a..edcd3f6 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -20,12 +20,10 @@
 
 import androidx.annotation.MainThread;
 
-import com.android.launcher3.Utilities;
+import kotlin.random.Random;
 
 import java.util.function.Supplier;
 
-import kotlin.random.Random;
-
 /**
  * A wrapper around {@link Trace} to allow better testing.
  *
@@ -67,9 +65,6 @@
     @SuppressWarnings("NewApi")
     @SuppressLint("NewApi")
     public SafeCloseable beginAsyncSection(String sectionName) {
-        if (!Utilities.ATLEAST_Q) {
-            return () -> { };
-        }
         int cookie = Random.Default.nextInt();
         Trace.beginAsyncSection(sectionName, cookie);
         return () -> Trace.endAsyncSection(sectionName, cookie);
@@ -81,9 +76,6 @@
     @SuppressWarnings("NewApi")
     @SuppressLint("NewApi")
     public SafeCloseable allowIpcs(String rpcName) {
-        if (!Utilities.ATLEAST_Q) {
-            return () -> { };
-        }
         int cookie = Random.Default.nextInt();
         Trace.beginAsyncSection(rpcName, cookie);
         return () -> Trace.endAsyncSection(rpcName, cookie);
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index 4f20bbc..fef1c72 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -28,12 +28,10 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.media.AudioAttributes;
-import android.os.Build;
 import android.os.SystemClock;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -48,7 +46,6 @@
 /**
  * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
  */
-@TargetApi(Build.VERSION_CODES.Q)
 public class VibratorWrapper {
 
     public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
@@ -138,7 +135,7 @@
             mThresholdUntilNextDragCallMillis = 0;
         }
 
-        if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported(
+        if (mVibrator.areAllPrimitivesSupported(
                 VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
                 VibrationEffect.Composition.PRIMITIVE_TICK)) {
             if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) {
@@ -226,8 +223,7 @@
     public void vibrate(int primitiveId, float primitiveScale, VibrationEffect fallbackEffect) {
         if (mHasVibrator && mIsHapticFeedbackEnabled) {
             UI_HELPER_EXECUTOR.execute(() -> {
-                if (Utilities.ATLEAST_R && primitiveId >= 0
-                        && mVibrator.areAllPrimitivesSupported(primitiveId)) {
+                if (primitiveId >= 0 && mVibrator.areAllPrimitivesSupported(primitiveId)) {
                     mVibrator.vibrate(VibrationEffect.startComposition()
                             .addPrimitive(primitiveId, primitiveScale)
                             .compose(), VIBRATION_ATTRS);
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 51a96c4..209a3dc 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -105,24 +105,7 @@
     /**
      * Returns the real bounds for the provided display after applying any insets normalization
      */
-    @TargetApi(Build.VERSION_CODES.R)
     public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) {
-        if (!Utilities.ATLEAST_R) {
-            Point smallestSize = new Point();
-            Point largestSize = new Point();
-            getDisplay(displayInfoContext).getCurrentSizeRange(smallestSize, largestSize);
-
-            if (info.size.y > info.size.x) {
-                // Portrait
-                return new WindowBounds(info.size.x, info.size.y, smallestSize.x, largestSize.y,
-                        info.rotation);
-            } else {
-                // Landscape
-                return new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y,
-                        info.rotation);
-            }
-        }
-
         WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics();
         Rect insets = new Rect();
@@ -133,10 +116,9 @@
     /**
      * Returns an updated insets, accounting for various Launcher UI specific overrides like taskbar
      */
-    @TargetApi(Build.VERSION_CODES.R)
     public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
             Rect outInsets) {
-        if (!Utilities.ATLEAST_R || !mTaskbarDrawnInProcess) {
+        if (!mTaskbarDrawnInProcess) {
             outInsets.set(oldInsets.getSystemWindowInsetLeft(), oldInsets.getSystemWindowInsetTop(),
                     oldInsets.getSystemWindowInsetRight(), oldInsets.getSystemWindowInsetBottom());
             return oldInsets;
@@ -220,8 +202,7 @@
         }
 
         boolean isTablet = swDp >= MIN_TABLET_WIDTH;
-        boolean isTabletOrGesture = isTablet
-                || (Utilities.ATLEAST_R && isGestureNav(context));
+        boolean isTabletOrGesture = isTablet || isGestureNav(context);
 
         // Use the status bar height resources because current system API to get the status bar
         // height doesn't allow to do this for an arbitrary display, it returns value only
@@ -360,17 +341,14 @@
     }
 
     /**
-     *
      * Returns the display associated with the context, or DEFAULT_DISPLAY if the context isn't
      * associated with a display.
      */
     protected Display getDisplay(Context displayInfoContext) {
-        if (Utilities.ATLEAST_R) {
-            try {
-                return displayInfoContext.getDisplay();
-            } catch (UnsupportedOperationException e) {
-                // Ignore
-            }
+        try {
+            return displayInfoContext.getDisplay();
+        } catch (UnsupportedOperationException e) {
+            // Ignore
         }
         return displayInfoContext.getSystemService(DisplayManager.class).getDisplay(
                 DEFAULT_DISPLAY);
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 0cddc97..c5317e3 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -19,7 +19,6 @@
 
 import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
 import static com.android.launcher3.Utilities.allowBGLaunch;
-import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_PENDING_INTENT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
@@ -266,32 +265,26 @@
         if (root == null) {
             return;
         }
-        if (Utilities.ATLEAST_R) {
-            Preconditions.assertUIThread();
-            //  Hide keyboard with WindowInsetsController if could. In case
-            //  hideSoftInputFromWindow may get ignored by input connection being finished
-            //  when the screen is off.
-            //
-            // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
-            // longer need to post to the message queue.
-            final WindowInsetsController wic = root.getWindowInsetsController();
-            WindowInsets insets = root.getRootWindowInsets();
-            boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
-            if (wic != null) {
-                // Only hide the keyboard if it is actually showing.
-                if (isImeShown) {
-                    StatsLogManager slm = getStatsLogManager();
-                    slm.keyboardStateManager().setKeyboardState(HIDE);
-
-                    // this method cannot be called cross threads
-                    wic.hide(WindowInsets.Type.ime());
-                    slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
-                }
-
-                // If the WindowInsetsController is not null, we end here regardless of whether we
-                // hid the keyboard or not.
-                return;
+        Preconditions.assertUIThread();
+        // Hide keyboard with WindowInsetsController if could. In case hideSoftInputFromWindow may
+        // get ignored by input connection being finished when the screen is off.
+        //
+        // In addition, inside IMF, the keyboards are closed asynchronously that launcher no longer
+        // need to post to the message queue.
+        final WindowInsetsController wic = root.getWindowInsetsController();
+        WindowInsets insets = root.getRootWindowInsets();
+        boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
+        if (wic != null) {
+            // Only hide the keyboard if it is actually showing.
+            if (isImeShown) {
+                // this method cannot be called cross threads
+                wic.hide(WindowInsets.Type.ime());
+                getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
             }
+
+            // If the WindowInsetsController is not null, we end here regardless of whether we hid
+            // the keyboard or not.
+            return;
         }
 
         InputMethodManager imm = root.getContext().getSystemService(InputMethodManager.class);
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index a1cd697..abc5ef8 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -551,25 +551,21 @@
 
     @Override
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
-        if (Utilities.ATLEAST_Q) {
-            Insets gestureInsets = insets.getMandatorySystemGestureInsets();
-            int gestureInsetBottom = gestureInsets.bottom;
-            Insets imeInset = Utilities.ATLEAST_R
-                    ? insets.getInsets(WindowInsets.Type.ime())
-                    : Insets.NONE;
-            DeviceProfile dp = mActivity.getDeviceProfile();
-            if (dp.isTaskbarPresent) {
-                // Ignore taskbar gesture insets to avoid interfering with TouchControllers.
-                gestureInsetBottom = ResourceUtils.getNavbarSize(
-                        ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources());
-            }
-            mSystemGestureRegion.set(
-                    Math.max(gestureInsets.left, imeInset.left),
-                    Math.max(gestureInsets.top, imeInset.top),
-                    Math.max(gestureInsets.right, imeInset.right),
-                    Math.max(gestureInsetBottom, imeInset.bottom)
-            );
+        Insets gestureInsets = insets.getMandatorySystemGestureInsets();
+        int gestureInsetBottom = gestureInsets.bottom;
+        Insets imeInset = insets.getInsets(WindowInsets.Type.ime());
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        if (dp.isTaskbarPresent) {
+            // Ignore taskbar gesture insets to avoid interfering with TouchControllers.
+            gestureInsetBottom = ResourceUtils.getNavbarSize(
+                    ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources());
         }
+        mSystemGestureRegion.set(
+                Math.max(gestureInsets.left, imeInset.left),
+                Math.max(gestureInsets.top, imeInset.top),
+                Math.max(gestureInsets.right, imeInset.right),
+                Math.max(gestureInsetBottom, imeInset.bottom)
+        );
         return super.dispatchApplyWindowInsets(insets);
     }
 }
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index 7737adb..5d3fa9b 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -25,7 +25,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -36,7 +35,6 @@
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
@@ -55,7 +53,6 @@
  * Supports springing just the foreground layer.
  * Supports clipping the icon to/from its icon shape.
  */
-@TargetApi(Build.VERSION_CODES.Q)
 public class ClipIconView extends View implements ClipPathView {
 
     private static final Rect sTmpRect = new Rect();
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 32c70a3..f76b53b 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -24,14 +24,12 @@
 import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
 
 import android.animation.Animator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.CancellationSignal;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -67,7 +65,6 @@
 /**
  * A view that is created to look like another view with the purpose of creating fluid animations.
  */
-@TargetApi(Build.VERSION_CODES.Q)
 public class FloatingIconView extends FrameLayout implements
         Animator.AnimatorListener, OnGlobalLayoutListener, FloatingView {
 
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index bfb75f0..76b6fde 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -18,14 +18,12 @@
 import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView;
 import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Picture;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
@@ -47,7 +45,6 @@
  * Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes
  * the surfaceHandle to the {@link GestureNavContract}.
  */
-@TargetApi(Build.VERSION_CODES.R)
 public class FloatingSurfaceView extends AbstractFloatingView implements
         OnGlobalLayoutListener, Insettable, SurfaceHolder.Callback2 {
 
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index c0b24fa..8408cc7 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -30,7 +30,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Property;
@@ -40,7 +39,6 @@
 import android.view.WindowInsets;
 import android.widget.TextView;
 
-import androidx.annotation.RequiresApi;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.FastScrollRecyclerView;
@@ -352,26 +350,21 @@
         float r = getScrollThumbRadius();
         mThumbBounds.set(-halfW, 0, halfW, mThumbHeight);
         canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
-        if (Utilities.ATLEAST_Q) {
-            mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
-            // swiping very close to the thumb area (not just within it's bound)
-            // will also prevent back gesture
-            SYSTEM_GESTURE_EXCLUSION_RECT.get(0).offset(mThumbDrawOffset.x, mThumbDrawOffset.y);
-            if (Utilities.ATLEAST_Q && mSystemGestureInsets != null) {
-                SYSTEM_GESTURE_EXCLUSION_RECT.get(0).left =
-                        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).right - mSystemGestureInsets.right;
-            }
-            setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
+        mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
+        // swiping very close to the thumb area (not just within it's bound)
+        // will also prevent back gesture
+        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).offset(mThumbDrawOffset.x, mThumbDrawOffset.y);
+        if (mSystemGestureInsets != null) {
+            SYSTEM_GESTURE_EXCLUSION_RECT.get(0).left =
+                    SYSTEM_GESTURE_EXCLUSION_RECT.get(0).right - mSystemGestureInsets.right;
         }
+        setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
         canvas.restoreToCount(saveCount);
     }
 
     @Override
-    @RequiresApi(Build.VERSION_CODES.Q)
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (Utilities.ATLEAST_Q) {
-            mSystemGestureInsets = insets.getSystemGestureInsets();
-        }
+        mSystemGestureInsets = insets.getSystemGestureInsets();
         return super.onApplyWindowInsets(insets);
     }
 
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index 80b1cdd..4f5d311 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.widget;
 
-import static com.android.launcher3.Utilities.ATLEAST_R;
-
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Insets;
@@ -153,17 +151,10 @@
     @SuppressLint("NewApi") // Already added API check.
     @Override
     public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
-        if (ATLEAST_R) {
-            Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
-            mInsets.set(insets.left, insets.top, insets.right, insets.bottom);
-        } else {
-            mInsets.set(windowInsets.getSystemWindowInsetLeft(),
-                    windowInsets.getSystemWindowInsetTop(),
-                    windowInsets.getSystemWindowInsetRight(),
-                    windowInsets.getSystemWindowInsetBottom());
-        }
-        mContent.setPadding(mContent.getPaddingStart(),
-                mContent.getPaddingTop(), mContent.getPaddingEnd(), mInsets.bottom);
+        Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
+        mInsets.set(insets.left, insets.top, insets.right, insets.bottom);
+        mContent.setPadding(mContent.getPaddingStart(), mContent.getPaddingTop(),
+                mContent.getPaddingEnd(), mInsets.bottom);
 
         int contentHorizontalMarginInPx = getResources().getDimensionPixelSize(
                 R.dimen.widget_list_horizontal_margin);
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5171fa2..9de7f62 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -158,10 +158,8 @@
     private int getNavBarScrimHeight(WindowInsets insets) {
         if (mDisableNavBarScrim) {
             return 0;
-        } else if (Utilities.ATLEAST_Q) {
-            return insets.getTappableElementInsets().bottom;
         } else {
-            return insets.getStableInsetBottom();
+            return insets.getTappableElementInsets().bottom;
         }
     }
 
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index e0de269..e77ec12 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,11 +16,9 @@
 
 package com.android.launcher3.widget;
 
-import android.annotation.TargetApi;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.graphics.Rect;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -44,7 +42,6 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -105,7 +102,7 @@
             setDefaultFocusHighlightEnabled(false);
         }
 
-        if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
+        if (Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
             setOnLightBackground(true);
         }
         mColorExtractor = new LocalColorExtractor(); // no-op
@@ -131,10 +128,9 @@
     }
 
     @Override
-    @TargetApi(Build.VERSION_CODES.Q)
     public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
         super.setAppWidget(appWidgetId, info);
-        if (!mTrackingWidgetUpdate && Utilities.ATLEAST_Q) {
+        if (!mTrackingWidgetUpdate) {
             mTrackingWidgetUpdate = true;
             Trace.beginAsyncSection(TRACE_METHOD_NAME + info.provider, appWidgetId);
             Log.i(TAG, "App widget created with id: " + appWidgetId);
@@ -142,9 +138,8 @@
     }
 
     @Override
-    @TargetApi(Build.VERSION_CODES.Q)
     public void updateAppWidget(RemoteViews remoteViews) {
-        if (mTrackingWidgetUpdate && remoteViews != null && Utilities.ATLEAST_Q) {
+        if (mTrackingWidgetUpdate && remoteViews != null) {
             Log.i(TAG, "App widget with id: " + getAppWidgetId() + " loaded");
             Trace.endAsyncSection(
                     TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId());
@@ -288,8 +283,7 @@
         super.onLayout(changed, left, top, right, bottom);
         mIsScrollable = checkScrollableRecursively(this);
 
-        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
-            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo info) {
             mTempRect.set(left, top, right, bottom);
             mColorExtractor.setWorkspaceLocation(mTempRect, (View) getParent(), info.screenId);
         }
@@ -425,8 +419,7 @@
 
     @Override
     protected boolean shouldAllowDirectClick() {
-        if (getTag() instanceof ItemInfo) {
-            ItemInfo item = (ItemInfo) getTag();
+        if (getTag() instanceof ItemInfo item) {
             return item.spanX == 1 && item.spanY == 1;
         }
         return false;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index ef51d15..3e4fd8c 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -15,7 +15,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -206,11 +205,7 @@
     }
 
     public int getWidgetFeatures() {
-        if (Utilities.ATLEAST_P) {
-            return widgetFeatures;
-        } else {
-            return 0;
-        }
+        return widgetFeatures;
     }
 
     public boolean isReconfigurable() {
diff --git a/tests/Android.bp b/tests/Android.bp
index a236954..310e418 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -21,13 +21,13 @@
 filegroup {
     name: "launcher-tests-src",
     srcs: [
-      "src/**/*.java",
-      "src/**/*.kt",
-      "multivalentTests/src/**/*.java",
-      "multivalentTests/src/**/*.kt",
+        "src/**/*.java",
+        "src/**/*.kt",
+        "multivalentTests/src/**/*.java",
+        "multivalentTests/src/**/*.kt",
     ],
     exclude_srcs: [
-        ":launcher-non-quickstep-tests-src"
+        ":launcher-non-quickstep-tests-src",
     ],
 }
 
@@ -35,25 +35,25 @@
 filegroup {
     name: "launcher-image-tests-helpers",
     srcs: [
-      "multivalentTests/src/com/android/launcher3/celllayout/board/*.java",
-      "multivalentTests/src/com/android/launcher3/celllayout/board/*.kt",
-      "multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java",
-      "multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
-      "multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
-      "multivalentTests/src/com/android/launcher3/ui/TestViewHelpers.java",
-      "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
-      "multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt",
-      "multivalentTests/src/com/android/launcher3/util/TestConstants.java",
-      "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
-      "multivalentTests/src/com/android/launcher3/util/Wait.java",
-      "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/*.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/*.kt",
-      "multivalentTests/src/com/android/launcher3/util/rule/*.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/*.kt",
-      "multivalentTests/src/com/android/launcher3/util/viewcapture_analysis/*.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/*.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/*.kt",
+        "multivalentTests/src/com/android/launcher3/celllayout/board/*.java",
+        "multivalentTests/src/com/android/launcher3/celllayout/board/*.kt",
+        "multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java",
+        "multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
+        "multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
+        "multivalentTests/src/com/android/launcher3/ui/TestViewHelpers.java",
+        "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
+        "multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt",
+        "multivalentTests/src/com/android/launcher3/util/TestConstants.java",
+        "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
+        "multivalentTests/src/com/android/launcher3/util/Wait.java",
+        "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/*.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/*.kt",
+        "multivalentTests/src/com/android/launcher3/util/rule/*.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/*.kt",
+        "multivalentTests/src/com/android/launcher3/util/viewcapture_analysis/*.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/*.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/*.kt",
     ],
 }
 
@@ -61,8 +61,8 @@
 filegroup {
     name: "launcher-non-quickstep-tests-src",
     srcs: [
-       "src/com/android/launcher3/nonquickstep/**/*.java",
-       "src/com/android/launcher3/nonquickstep/**/*.kt",
+        "src/com/android/launcher3/nonquickstep/**/*.java",
+        "src/com/android/launcher3/nonquickstep/**/*.kt",
     ],
 }
 
@@ -70,42 +70,42 @@
 filegroup {
     name: "launcher-oop-tests-src",
     srcs: [
-      "src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java",
-      "src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java",
-      "src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java",
-      "src/com/android/launcher3/dragging/TaplDragTest.java",
-      "src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java",
-      "multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
-      "multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
-      "src/com/android/launcher3/ui/TaplTestsLauncher3Test.java",
-      "src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java",
-      "src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java",
-      "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
-      "multivalentTests/src/com/android/launcher3/util/TestConstants.java",
-      "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
-      "multivalentTests/src/com/android/launcher3/util/Wait.java",
-      "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
-      "multivalentTests/src/com/android/launcher3/util/rule/SamplerRule.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/ScreenRecordRule.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/ShellCommandRule.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/TestIsolationRule.java",
-      "multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java",
-      "multivalentTests/src/com/android/launcher3/util/viewcapture_analysis/*.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
-      "multivalentTests/src/com/android/launcher3/testcomponent/ImeTestActivity.java",
+        "src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java",
+        "src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java",
+        "src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java",
+        "src/com/android/launcher3/dragging/TaplDragTest.java",
+        "src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java",
+        "multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
+        "multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
+        "src/com/android/launcher3/ui/TaplTestsLauncher3Test.java",
+        "src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java",
+        "src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java",
+        "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
+        "multivalentTests/src/com/android/launcher3/util/TestConstants.java",
+        "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
+        "multivalentTests/src/com/android/launcher3/util/Wait.java",
+        "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/FailureWatcher.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
+        "multivalentTests/src/com/android/launcher3/util/rule/SamplerRule.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/ScreenRecordRule.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/ShellCommandRule.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/TestIsolationRule.java",
+        "multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java",
+        "multivalentTests/src/com/android/launcher3/util/viewcapture_analysis/*.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
+        "multivalentTests/src/com/android/launcher3/testcomponent/ImeTestActivity.java",
     ],
 }
 
 // Library with all the dependencies for building quickstep
 android_library {
     name: "Launcher3TestLib",
-    srcs: [ ],
+    srcs: [],
     asset_dirs: ["assets"],
     resource_dirs: ["res"],
     static_libs: [
@@ -130,11 +130,15 @@
     ],
     manifest: "AndroidManifest-common.xml",
     platform_apis: true,
+    // TODO(b/319712088): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 android_library {
     name: "Launcher3TestResources",
     resource_dirs: ["res"],
+    // TODO(b/319712088): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 android_test {
@@ -171,24 +175,24 @@
 android_library {
     name: "launcher-testing-shared",
     srcs: [
-        "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java"
+        "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java",
     ],
-    resource_dirs: [ ],
+    resource_dirs: [],
     manifest: "multivalentTests/shared/AndroidManifest.xml",
     sdk_version: "current",
     min_sdk_version: min_launcher3_sdk_version,
- }
+}
 
 filegroup {
     name: "launcher-testing-helpers",
     srcs: [
-      "src/**/*.java",
-      "src/**/*.kt",
-      "multivalentTests/src/**/*.java",
-      "multivalentTests/src/**/*.kt",
-      "multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
-      "multivalentTests/tapl/com/android/launcher3/tapl/*.java",
-      "multivalentTests/tapl/com/android/launcher3/tapl/*.kt",
+        "src/**/*.java",
+        "src/**/*.kt",
+        "multivalentTests/src/**/*.java",
+        "multivalentTests/src/**/*.kt",
+        "multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
+        "multivalentTests/tapl/com/android/launcher3/tapl/*.java",
+        "multivalentTests/tapl/com/android/launcher3/tapl/*.kt",
     ],
     exclude_srcs: [
         // Test classes
@@ -209,6 +213,8 @@
     static_libs: [
         "Launcher3CommonDepsLib",
     ],
+    // TODO(b/319712088): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 android_robolectric_test {
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index e5e3354..7059268 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -88,6 +88,14 @@
                 android:resource="@xml/appwidget_dynamic_colors"/>
         </receiver>
 
+        <receiver android:name="com.android.launcher3.testcomponent.UnarchiveBroadcastReceiver"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.UNARCHIVE_PACKAGE"/>
+            </intent-filter>
+        </receiver>
+
         <activity
             android:name="com.android.launcher3.testcomponent.WidgetConfigActivity"
             android:exported="true">
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index daace8e..5cf96c8 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -23,14 +23,6 @@
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
-
-        <receiver android:name="com.android.launcher3.compat.TaplPromiseIconUiTest$UnarchiveBroadcastReceiver"
-                  android:enabled="true"
-                  android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.UNARCHIVE_PACKAGE"/>
-            </intent-filter>
-        </receiver>
     </application>
 
     <instrumentation
diff --git a/tests/multivalentTests/src/com/android/launcher3/testcomponent/UnarchiveBroadcastReceiver.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/UnarchiveBroadcastReceiver.java
new file mode 100644
index 0000000..0f5117b
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/testcomponent/UnarchiveBroadcastReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 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.testcomponent;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Broadcast Receiver to receive app unarchival broadcast. It is used to fulfill archiving
+ * platform requirements.
+ */
+public class UnarchiveBroadcastReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+    }
+}
diff --git a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
index 3068785..423ca24 100644
--- a/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
+++ b/tests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
@@ -19,6 +19,7 @@
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
@@ -63,9 +64,10 @@
 
     private static final int PRIVATE_SPACE_HEADER_ITEM_COUNT = 1;
     private static final int MAIN_USER_APP_COUNT = 2;
-    private static final int PRIVATE_USER_APP_COUNT = 1;
+    private static final int PRIVATE_USER_APP_COUNT = 2;
     private static final int NUM_APP_COLS = 4;
     private static final int NUM_APP_ROWS = 3;
+    private static final int PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT = 1;
 
     private AlphabeticalAppsList<?> mAlphabeticalAppsList;
     @Mock
@@ -96,6 +98,10 @@
         when(mPrivateProfileManager.addPrivateSpaceHeader(any()))
                 .thenAnswer(answer(this::addPrivateSpaceHeader));
         when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+                .thenReturn(iteminfo -> iteminfo.componentName == null
+                        || !iteminfo.componentName.getPackageName()
+                        .equals("com.android.launcher3.tests.camera"));
 
         mAlphabeticalAppsList.updateItemFilter(info -> info != null
                 && info.user.equals(MAIN_HANDLE));
@@ -112,6 +118,44 @@
     }
 
     @Test
+    public void privateProfileEnabled_privateProfileAppsShownWithSeparator() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_PRIVATE_SPACE_SYS_APPS_SEPARATION);
+        when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser());
+        when(mPrivateProfileManager.addPrivateSpaceHeader(any()))
+                .thenAnswer(answer(this::addPrivateSpaceHeader));
+        when(mPrivateProfileManager.addSystemAppsDivider(any()))
+                .thenAnswer(answer(this::addSystemAppsDivider));
+        when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+        when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+                .thenReturn(iteminfo -> iteminfo.componentName == null
+                        || !iteminfo.componentName.getPackageName()
+                        .equals("com.android.launcher3.tests.camera"));
+
+        mAlphabeticalAppsList.updateItemFilter(info -> info != null
+                && info.user.equals(MAIN_HANDLE));
+
+        assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT
+                + PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT
+                + PRIVATE_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size());
+        assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT,
+                mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
+                        item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size());
+        assertEquals(PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT,
+                mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
+                        item.viewType == VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER).toList().size());
+        List<BaseAllAppsAdapter.AdapterItem> psApps = mAlphabeticalAppsList.getAdapterItems()
+                .stream()
+                .filter(item -> item.itemInfo != null && item.itemInfo.user.equals(PRIVATE_HANDLE))
+                .toList();
+        assertEquals(PRIVATE_USER_APP_COUNT, psApps.size());
+        assert psApps.get(0).itemInfo.title != null;
+        assertEquals("Private Messenger", psApps.get(0).itemInfo.title.toString());
+        assert psApps.get(1).itemInfo.title != null;
+        assertEquals("Private Camera", psApps.get(1).itemInfo.title.toString());
+    }
+
+    @Test
     public void privateProfileDisabled_onlyPrivateProfileHeaderViewIsPresent() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE);
         when(mAllAppsStore.getApps()).thenReturn(createAppInfoListForMainAndPrivateUser());
@@ -281,6 +325,12 @@
         return adapterItemList.size();
     }
 
+    private int addSystemAppsDivider(List<BaseAllAppsAdapter.AdapterItem> adapterItemList) {
+        adapterItemList.add(new BaseAllAppsAdapter
+                .AdapterItem(VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER));
+        return adapterItemList.size();
+    }
+
     private AppInfo[] createAppInfoListForMainUser() {
         ComponentName gmailComponentName = new ComponentName(mContext,
                 "com.android.launcher3.tests.Activity" + "Gmail");
@@ -298,7 +348,11 @@
                 "com.android.launcher3.tests.Activity" + "PrivateMessenger");
         AppInfo privateMessengerAppInfo = new AppInfo(privateMessengercomponentName,
                 "Private Messenger", PRIVATE_HANDLE, new Intent());
-        return new AppInfo[]{privateMessengerAppInfo};
+        ComponentName privateCameraComponentName = new ComponentName(
+                "com.android.launcher3.tests.camera", "CameraActivity");
+        AppInfo privateCameraAppInfo = new AppInfo(privateCameraComponentName,
+                "Private Camera", PRIVATE_HANDLE, new Intent());
+        return new AppInfo[]{privateMessengerAppInfo, privateCameraAppInfo};
     }
 
     private AppInfo[] createAppInfoListForMainAndPrivateUser() {
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 69edd0f..fd8739c 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.allapps;
 
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
 import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED;
 import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
@@ -31,8 +33,10 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Process;
@@ -43,6 +47,7 @@
 
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.ActivityContextWrapper;
 import com.android.launcher3.util.UserIconInfo;
 import com.android.launcher3.util.rule.TestStabilityRule;
 
@@ -56,6 +61,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
@@ -89,6 +95,8 @@
     private AllAppsStore mAllAppsStore;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private LauncherApps mLauncherApps;
 
     private boolean mRunnableCalled = false;
 
@@ -103,6 +111,13 @@
         when(mActivityAllAppsContainerView.getAppsStore()).thenReturn(mAllAppsStore);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.resolveActivity(any(), any())).thenReturn(new ResolveInfo());
+        when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
+        when(mLauncherApps.getAppMarketActivityIntent(any(), any())).thenReturn(PendingIntent
+                .getActivity(new ActivityContextWrapper(getApplicationContext()), 0,
+                        new Intent(), PendingIntent.FLAG_IMMUTABLE).getIntentSender());
+        when(mContext.getPackageName())
+                .thenReturn("com.android.launcher3.tests.privateProfileManager");
+        when(mLauncherApps.getPreInstalledSystemPackages(any())).thenReturn(new ArrayList<>());
         mPrivateProfileManager = new PrivateProfileManager(mUserManager,
                 mActivityAllAppsContainerView, mStatsLogManager, mUserCache);
     }
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index e1af774..0ff7c20 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -29,8 +29,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.MultipageCellLayout;
 import com.android.launcher3.celllayout.board.CellLayoutBoard;
 import com.android.launcher3.celllayout.board.IconPoint;
@@ -41,8 +39,7 @@
 import com.android.launcher3.util.ActivityContextWrapper;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 
-import org.junit.After;
-import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -70,7 +67,8 @@
     private static final int TOTAL_OF_CASES_GENERATED = 300;
     private Context mApplicationContext;
 
-    private int mPrevNumColumns, mPrevNumRows;
+    @Rule
+    public UnitTestCellLayoutBuilderRule mCellLayoutBuilder = new UnitTestCellLayoutBuilderRule();
 
     /**
      * This test reads existing test cases and makes sure the CellLayout produces the same
@@ -144,34 +142,10 @@
                 (CellLayoutLayoutParams) cell.getLayoutParams(), true);
     }
 
-    public CellLayout createCellLayout(int width, int height, boolean isMulti) {
-        Context c = mApplicationContext;
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
-        // modify the device profile.
-        dp.inv.numColumns = isMulti ? width / 2 : width;
-        dp.inv.numRows = height;
-        dp.cellLayoutBorderSpacePx = new Point(0, 0);
-
-        CellLayout cl = isMulti ? new MultipageCellLayout(getWrappedContext(c, dp))
-                : 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));
-        return cl;
-    }
-
-    private Context getWrappedContext(Context context, DeviceProfile dp) {
-        return new ActivityContextWrapper(context) {
-            public DeviceProfile getDeviceProfile() {
-                return dp;
-            }
-        };
-    }
-
     public ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX,
             int spanY, int minSpanX, int minSpanY, boolean isMulti) {
-        CellLayout cl = createCellLayout(board.getWidth(), board.getHeight(), isMulti);
+        CellLayout cl = mCellLayoutBuilder.createCellLayout(board.getWidth(), board.getHeight(),
+                isMulti);
 
         // The views have to be sorted or the result can vary
         board.getIcons()
@@ -249,22 +223,6 @@
         }
     }
 
-    @Before
-    public void storePreviousValues() {
-        Context c = new ActivityContextWrapper(getApplicationContext());
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
-        mPrevNumColumns = dp.inv.numColumns;
-        mPrevNumRows = dp.inv.numRows;
-    }
-
-    @After
-    public void restorePreviousValues() {
-        Context c = new ActivityContextWrapper(getApplicationContext());
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
-        dp.inv.numColumns = mPrevNumColumns;
-        dp.inv.numRows = mPrevNumRows;
-    }
-
     private ReorderAlgorithmUnitTestCase generateRandomTestCase(
             RandomBoardGenerator boardGenerator) {
         ReorderAlgorithmUnitTestCase testCase = new ReorderAlgorithmUnitTestCase();
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt b/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
new file mode 100644
index 0000000..0bec1b2
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 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.celllayout
+
+import android.content.Context
+import android.util.ArrayMap
+import android.view.View
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.CellLayout
+import com.android.launcher3.Reorderable
+import com.android.launcher3.celllayout.ReorderPreviewAnimation.Companion.HINT_DURATION
+import com.android.launcher3.celllayout.ReorderPreviewAnimation.Companion.PREVIEW_DURATION
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.MultiTranslateDelegate
+import com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_BOUNCE_OFFSET
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+class Mock(context: Context) : Reorderable, View(context) {
+
+    init {
+        mLeft = 0
+        mRight = 100
+    }
+
+    private val translateDelegate = MultiTranslateDelegate(this)
+
+    private var scaleForReorderBounce = 1f
+    override fun getTranslateDelegate(): MultiTranslateDelegate {
+        return translateDelegate
+    }
+
+    override fun setReorderBounceScale(scale: Float) {
+        scaleForReorderBounce = scale
+    }
+
+    override fun getReorderBounceScale(): Float {
+        return scaleForReorderBounce
+    }
+
+    fun toAnimationValues(): AnimationValues {
+        return AnimationValues(
+            (translateDelegate.getTranslationX(INDEX_REORDER_BOUNCE_OFFSET).value * 100).toInt(),
+            (translateDelegate.getTranslationY(INDEX_REORDER_BOUNCE_OFFSET).value * 100).toInt(),
+            (scaleForReorderBounce * 100).toInt()
+        )
+    }
+}
+
+data class AnimationValues(val dx: Int, val dy: Int, val scale: Int)
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ReorderPreviewAnimationTest {
+
+    @JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule()
+
+    private val applicationContext =
+        ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+    /**
+     * @param animationTime the time of the animation we will check the state against.
+     * @param mode the mode either PREVIEW_DURATION or HINT_DURATION.
+     * @param valueToMatch the state of the animation we expect to see at animationTime.
+     * @param isAfterReverse if the animation is finish and we are returning to the beginning.
+     */
+    private fun testAnimationAtGivenProgress(
+        animationTime: Int,
+        mode: Int,
+        valueToMatch: AnimationValues
+    ) {
+        val view = Mock(applicationContext)
+        val cellLayout = cellLayoutBuilder.createCellLayout(100, 100, false)
+        val map = ArrayMap<Reorderable, ReorderPreviewAnimation<Mock>>()
+        val animation =
+            ReorderPreviewAnimation(
+                view,
+                mode,
+                3,
+                3,
+                1,
+                7,
+                1,
+                1,
+                CellLayout.REORDER_PREVIEW_MAGNITUDE,
+                cellLayout,
+                map
+            )
+        // Remove delay because it's randomly generated and it can slightly change the results.
+        animation.animator.startDelay = 0
+        animation.animator.currentPlayTime = animationTime.toLong()
+        val currentValue = view.toAnimationValues()
+        assert(currentValue == valueToMatch) {
+            "The value of the animation $currentValue at $animationTime time (milliseconds) doesn't match the given value $valueToMatch"
+        }
+    }
+
+    @Test
+    fun testAnimationModePreview() {
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION * 0,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 0, dy = 0, scale = 100)
+        )
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION / 2,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 2, dy = -5, scale = 98)
+        )
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION / 3,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 1, dy = -2, scale = 99)
+        )
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 5, dy = -10, scale = 96)
+        )
+
+        // MODE_PREVIEW oscillates and goes back to 0,0
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION * 2,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 0, dy = 0, scale = 100)
+        )
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION * 99,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 5, dy = -10, scale = 96)
+        )
+        testAnimationAtGivenProgress(
+            PREVIEW_DURATION * 98,
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 0, dy = 0, scale = 100)
+        )
+        testAnimationAtGivenProgress(
+            (PREVIEW_DURATION * 1.5).toInt(),
+            ReorderPreviewAnimation.MODE_PREVIEW,
+            AnimationValues(dx = 2, dy = -5, scale = 98)
+        )
+    }
+
+    @Test
+    fun testAnimationModeHint() {
+        testAnimationAtGivenProgress(
+            HINT_DURATION * 0,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = 0, dy = 0, scale = 100)
+        )
+        testAnimationAtGivenProgress(
+            HINT_DURATION,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = -5, dy = 10, scale = 96)
+        )
+        testAnimationAtGivenProgress(
+            HINT_DURATION / 2,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = -2, dy = 5, scale = 98)
+        )
+        testAnimationAtGivenProgress(
+            HINT_DURATION / 3,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = -1, dy = 2, scale = 99)
+        )
+        testAnimationAtGivenProgress(
+            HINT_DURATION,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = -5, dy = 10, scale = 96)
+        )
+
+        // After one cycle the animationValues should always be the top values and don't cycle.
+        testAnimationAtGivenProgress(
+            HINT_DURATION * 2,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = -5, dy = 10, scale = 96)
+        )
+        testAnimationAtGivenProgress(
+            HINT_DURATION * 99,
+            ReorderPreviewAnimation.MODE_HINT,
+            AnimationValues(dx = -5, dy = 10, scale = 96)
+        )
+    }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt b/tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
new file mode 100644
index 0000000..0f0dd65
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 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.celllayout
+
+import android.content.Context
+import android.graphics.Point
+import android.view.View
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.CellLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.MultipageCellLayout
+import com.android.launcher3.util.ActivityContextWrapper
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+/**
+ * Create CellLayouts to be used in Unit testing and make sure to set the DeviceProfile back to
+ * normal.
+ */
+class UnitTestCellLayoutBuilderRule : TestWatcher() {
+
+    private var prevNumColumns = 0
+    private var prevNumRows = 0
+
+    private val applicationContext =
+        ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+    override fun starting(description: Description?) {
+        val dp = getDeviceProfile()
+        prevNumColumns = dp.inv.numColumns
+        prevNumRows = dp.inv.numRows
+    }
+
+    override fun finished(description: Description?) {
+        val dp = getDeviceProfile()
+        dp.inv.numColumns = prevNumColumns
+        dp.inv.numRows = prevNumRows
+    }
+
+    fun createCellLayout(width: Int, height: Int, isMulti: Boolean): CellLayout {
+        val dp = getDeviceProfile()
+        // modify the device profile.
+        dp.inv.numColumns = if (isMulti) width / 2 else width
+        dp.inv.numRows = height
+        dp.cellLayoutBorderSpacePx = Point(0, 0)
+        val cl =
+            if (isMulti) MultipageCellLayout(getWrappedContext(applicationContext, dp))
+            else CellLayout(getWrappedContext(applicationContext, 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)
+        )
+        return cl
+    }
+
+    private fun getDeviceProfile(): DeviceProfile =
+        InvariantDeviceProfile.INSTANCE[applicationContext].getDeviceProfile(applicationContext)
+            .copy(applicationContext)
+
+    private fun getWrappedContext(context: Context, dp: DeviceProfile): Context {
+        return object : ActivityContextWrapper(context) {
+            override fun getDeviceProfile(): DeviceProfile {
+                return dp
+            }
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
index f1b0441..7ed0fa5 100644
--- a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java
@@ -19,9 +19,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
@@ -170,13 +167,6 @@
         mSessionId = -1;
     }
 
-    // Dummy receiver to fulfill archiving platform requirements, unused in reality.
-    public static class UnarchiveBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-        }
-    }
-
     private void installDummyAppAndWaitForUIUpdate() throws IOException {
         TestUtil.installDummyApp();
         mLauncher.waitForModelQueueCleared();
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index cb57918..e2ca31f 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -95,6 +95,10 @@
     @Test
     fun loadsDataProperly() =
         with(BgDataModel()) {
+            val MAIN_HANDLE = UserHandle.of(0)
+            val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
+            `when`(userCache.userProfiles).thenReturn(mockUserHandles)
+            `when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
             LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder)
                 .runSyncOnBackgroundThread()
             Truth.assertThat(workspaceItems.size).isAtLeast(25)